<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<script id="versionArea" type="text/javascript">
//<![CDATA[
var version = {title: "TiddlyWiki", major: 2, minor: 6, revision: 2, date: new Date("Jan 6, 2011"), extensions: {}};
//]]>
</script>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<link rel="shortcut icon" href="favicon.ico">
<meta name="copyright" content="
TiddlyWiki created by Jeremy Ruston, (jeremy [at] osmosoft [dot] com)

Copyright (c) Jeremy Ruston 2004-2007
Copyright (c) UnaMesa Association 2007-2011

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.

Neither the name of the UnaMesa Association nor the names of its contributors may be
used to endorse or promote products derived from this software without specific
prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
" />
<!--PRE-HEAD-START-->
<!--{{{-->
<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml' />
<!--}}}-->

<!--PRE-HEAD-END-->
<title> OpenResty - 一个通过扩展 Nginx 实现的强大的 web 应用服务器 </title>
<style id="styleArea" type="text/css">
#saveTest {display:none;}
#messageArea {display:none;}
#copyright {display:none;}
#storeArea {display:none;}
#storeArea div {padding:0.5em; margin:1em 0em 0em 0em; border-color:#fff #666 #444 #ddd; border-style:solid; border-width:2px; overflow:auto;}
#shadowArea {display:none;}
#javascriptWarning {width:100%; text-align:center; font-weight:bold; background-color:#dd1100; color:#fff; padding:1em 0em;}
</style>
<!--POST-HEAD-START-->

<!--POST-HEAD-END-->
</head>
<body onload="main();" onunload="if(window.unload) unload();">
<!--PRE-BODY-START-->

<!--PRE-BODY-END-->
<div id="copyright">
Welcome to TiddlyWiki created by Jeremy Ruston; Copyright &copy; 2004-2007 Jeremy Ruston, Copyright &copy; 2007-2011 UnaMesa Association
</div>
<noscript>
<div id="javascriptWarning">
This page requires JavaScript to function properly.<br /><br />If you are using Microsoft Internet Explorer you may need to click on the yellow bar above and select 'Allow Blocked Content'. You must then click 'Yes' on the following security warning.
</div>
</noscript>
<div id="saveTest"></div>
<div id="backstageCloak"></div>
<div id="backstageButton"></div>
<div id="backstageArea"><div id="backstageToolbar"></div></div>
<div id="backstage">
	<div id="backstagePanel"></div>
</div>
<div id="contentWrapper"></div>
<div id="contentStash"></div>
<div id="shadowArea">
<div title="MarkupPreHead">
<pre>&lt;!--{{{--&gt;
&lt;link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml' /&gt;
&lt;!--}}}--&gt;
</pre>
</div>
<div title="ColorPalette">
<pre>Background: #fff
Foreground: #000
PrimaryPale: #8cf
PrimaryLight: #18f
PrimaryMid: #04b
PrimaryDark: #014
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88
</pre>
</div>
<div title="StyleSheetColors">
<pre>/*{{{*/
body {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}

a {color:[[ColorPalette::PrimaryMid]];}
a:hover {background-color:[[ColorPalette::PrimaryMid]]; color:[[ColorPalette::Background]];}
a img {border:0;}

h1,h2,h3,h4,h5,h6 {color:[[ColorPalette::SecondaryDark]]; background:transparent;}
h1 {border-bottom:2px solid [[ColorPalette::TertiaryLight]];}
h2,h3 {border-bottom:1px solid [[ColorPalette::TertiaryLight]];}

.button {color:[[ColorPalette::PrimaryDark]]; border:1px solid [[ColorPalette::Background]];}
.button:hover {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::SecondaryLight]]; border-color:[[ColorPalette::SecondaryMid]];}
.button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::SecondaryDark]];}

.header {background:[[ColorPalette::PrimaryMid]];}
.headerShadow {color:[[ColorPalette::Foreground]];}
.headerShadow a {font-weight:normal; color:[[ColorPalette::Foreground]];}
.headerForeground {color:[[ColorPalette::Background]];}
.headerForeground a {font-weight:normal; color:[[ColorPalette::PrimaryPale]];}

.tabSelected{color:[[ColorPalette::PrimaryDark]];
	background:[[ColorPalette::TertiaryPale]];
	border-left:1px solid [[ColorPalette::TertiaryLight]];
	border-top:1px solid [[ColorPalette::TertiaryLight]];
	border-right:1px solid [[ColorPalette::TertiaryLight]];
}
.tabUnselected {color:[[ColorPalette::Background]]; background:[[ColorPalette::TertiaryMid]];}
.tabContents {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::TertiaryPale]]; border:1px solid [[ColorPalette::TertiaryLight]];}
.tabContents .button {border:0;}

#sidebar {}
#sidebarOptions input {border:1px solid [[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel {background:[[ColorPalette::PrimaryPale]];}
#sidebarOptions .sliderPanel a {border:none;color:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:hover {color:[[ColorPalette::Background]]; background:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:active {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::Background]];}

.wizard {background:[[ColorPalette::PrimaryPale]]; border:1px solid [[ColorPalette::PrimaryMid]];}
.wizard h1 {color:[[ColorPalette::PrimaryDark]]; border:none;}
.wizard h2 {color:[[ColorPalette::Foreground]]; border:none;}
.wizardStep {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];
	border:1px solid [[ColorPalette::PrimaryMid]];}
.wizardStep.wizardStepDone {background:[[ColorPalette::TertiaryLight]];}
.wizardFooter {background:[[ColorPalette::PrimaryPale]];}
.wizardFooter .status {background:[[ColorPalette::PrimaryDark]]; color:[[ColorPalette::Background]];}
.wizard .button {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryLight]]; border: 1px solid;
	border-color:[[ColorPalette::SecondaryPale]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryPale]];}
.wizard .button:hover {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Background]];}
.wizard .button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::Foreground]]; border: 1px solid;
	border-color:[[ColorPalette::PrimaryDark]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryDark]];}

.wizard .notChanged {background:transparent;}
.wizard .changedLocally {background:#80ff80;}
.wizard .changedServer {background:#8080ff;}
.wizard .changedBoth {background:#ff8080;}
.wizard .notFound {background:#ffff80;}
.wizard .putToServer {background:#ff80ff;}
.wizard .gotFromServer {background:#80ffff;}

#messageArea {border:1px solid [[ColorPalette::SecondaryMid]]; background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]];}
#messageArea .button {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::SecondaryPale]]; border:none;}

.popupTiddler {background:[[ColorPalette::TertiaryPale]]; border:2px solid [[ColorPalette::TertiaryMid]];}

.popup {background:[[ColorPalette::TertiaryPale]]; color:[[ColorPalette::TertiaryDark]]; border-left:1px solid [[ColorPalette::TertiaryMid]]; border-top:1px solid [[ColorPalette::TertiaryMid]]; border-right:2px solid [[ColorPalette::TertiaryDark]]; border-bottom:2px solid [[ColorPalette::TertiaryDark]];}
.popup hr {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::PrimaryDark]]; border-bottom:1px;}
.popup li.disabled {color:[[ColorPalette::TertiaryMid]];}
.popup li a, .popup li a:visited {color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:active {background:[[ColorPalette::SecondaryPale]]; color:[[ColorPalette::Foreground]]; border: none;}
.popupHighlight {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
.listBreak div {border-bottom:1px solid [[ColorPalette::TertiaryDark]];}

.tiddler .defaultCommand {font-weight:bold;}

.shadow .title {color:[[ColorPalette::TertiaryDark]];}

.title {color:[[ColorPalette::SecondaryDark]];}
.subtitle {color:[[ColorPalette::TertiaryDark]];}

.toolbar {color:[[ColorPalette::PrimaryMid]];}
.toolbar a {color:[[ColorPalette::TertiaryLight]];}
.selected .toolbar a {color:[[ColorPalette::TertiaryMid]];}
.selected .toolbar a:hover {color:[[ColorPalette::Foreground]];}

.tagging, .tagged {border:1px solid [[ColorPalette::TertiaryPale]]; background-color:[[ColorPalette::TertiaryPale]];}
.selected .tagging, .selected .tagged {background-color:[[ColorPalette::TertiaryLight]]; border:1px solid [[ColorPalette::TertiaryMid]];}
.tagging .listTitle, .tagged .listTitle {color:[[ColorPalette::PrimaryDark]];}
.tagging .button, .tagged .button {border:none;}

.footer {color:[[ColorPalette::TertiaryLight]];}
.selected .footer {color:[[ColorPalette::TertiaryMid]];}

.sparkline {background:[[ColorPalette::PrimaryPale]]; border:0;}
.sparktick {background:[[ColorPalette::PrimaryDark]];}

.error, .errorButton {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Error]];}
.warning {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryPale]];}
.lowlight {background:[[ColorPalette::TertiaryLight]];}

.zoomer {background:none; color:[[ColorPalette::TertiaryMid]]; border:3px solid [[ColorPalette::TertiaryMid]];}

.imageLink, #displayArea .imageLink {background:transparent;}

.annotation {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border:2px solid [[ColorPalette::SecondaryMid]];}

.viewer .listTitle {list-style-type:none; margin-left:-2em;}
.viewer .button {border:1px solid [[ColorPalette::SecondaryMid]];}
.viewer blockquote {border-left:3px solid [[ColorPalette::TertiaryDark]];}

.viewer table, table.twtable {border:2px solid [[ColorPalette::TertiaryDark]];}
.viewer th, .viewer thead td, .twtable th, .twtable thead td {background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::Background]];}
.viewer td, .viewer tr, .twtable td, .twtable tr {border:1px solid [[ColorPalette::TertiaryDark]];}

.viewer pre {border:1px solid [[ColorPalette::SecondaryLight]]; background:[[ColorPalette::SecondaryPale]];}
.viewer code {color:[[ColorPalette::SecondaryDark]];}
.viewer hr {border:0; border-top:dashed 1px [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::TertiaryDark]];}

.highlight, .marked {background:[[ColorPalette::SecondaryLight]];}

.editor input {border:1px solid [[ColorPalette::PrimaryMid]];}
.editor textarea {border:1px solid [[ColorPalette::PrimaryMid]]; width:100%;}
.editorFooter {color:[[ColorPalette::TertiaryMid]];}
.readOnly {background:[[ColorPalette::TertiaryPale]];}

#backstageArea {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::TertiaryMid]];}
#backstageArea a {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstageArea a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; }
#backstageArea a.backstageSelTab {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
#backstageButton a {background:none; color:[[ColorPalette::Background]]; border:none;}
#backstageButton a:hover {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstagePanel {background:[[ColorPalette::Background]]; border-color: [[ColorPalette::Background]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]];}
.backstagePanelFooter .button {border:none; color:[[ColorPalette::Background]];}
.backstagePanelFooter .button:hover {color:[[ColorPalette::Foreground]];}
#backstageCloak {background:[[ColorPalette::Foreground]]; opacity:0.6; filter:'alpha(opacity=60)';}
/*}}}*/</pre>
</div>
<div title="StyleSheetLayout">
<pre>/*{{{*/
* html .tiddler {height:1%;}

body {font-size:.75em; font-family:arial,helvetica; margin:0; padding:0;}

h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
h1,h2,h3 {padding-bottom:1px; margin-top:1.2em;margin-bottom:0.3em;}
h4,h5,h6 {margin-top:1em;}
h1 {font-size:1.35em;}
h2 {font-size:1.25em;}
h3 {font-size:1.1em;}
h4 {font-size:1em;}
h5 {font-size:.9em;}

hr {height:1px;}

a {text-decoration:none;}

dt {font-weight:bold;}

ol {list-style-type:decimal;}
ol ol {list-style-type:lower-alpha;}
ol ol ol {list-style-type:lower-roman;}
ol ol ol ol {list-style-type:decimal;}
ol ol ol ol ol {list-style-type:lower-alpha;}
ol ol ol ol ol ol {list-style-type:lower-roman;}
ol ol ol ol ol ol ol {list-style-type:decimal;}

.txtOptionInput {width:11em;}

#contentWrapper .chkOptionInput {border:0;}

.externalLink {text-decoration:underline;}

.indent {margin-left:3em;}
.outdent {margin-left:3em; text-indent:-3em;}
code.escaped {white-space:nowrap;}

.tiddlyLinkExisting {font-weight:bold;}
.tiddlyLinkNonExisting {font-style:italic;}

/* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
a.tiddlyLinkNonExisting.shadow {font-weight:bold;}

#mainMenu .tiddlyLinkExisting,
	#mainMenu .tiddlyLinkNonExisting,
	#sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
#sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}

.header {position:relative;}
.header a:hover {background:transparent;}
.headerShadow {position:relative; padding:4.5em 0 1em 1em; left:-1px; top:-1px;}
.headerForeground {position:absolute; padding:4.5em 0 1em 1em; left:0px; top:0px;}

.siteTitle {font-size:3em;}
.siteSubtitle {font-size:1.2em;}

#mainMenu {position:absolute; left:0; width:10em; text-align:right; line-height:1.6em; padding:1.5em 0.5em 0.5em 0.5em; font-size:1.1em;}

#sidebar {position:absolute; right:3px; width:16em; font-size:.9em;}
#sidebarOptions {padding-top:0.3em;}
#sidebarOptions a {margin:0 0.2em; padding:0.2em 0.3em; display:block;}
#sidebarOptions input {margin:0.4em 0.5em;}
#sidebarOptions .sliderPanel {margin-left:1em; padding:0.5em; font-size:.85em;}
#sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:0;}
#sidebarOptions .sliderPanel input {margin:0 0 0.3em 0;}
#sidebarTabs .tabContents {width:15em; overflow:hidden;}

.wizard {padding:0.1em 1em 0 2em;}
.wizard h1 {font-size:2em; font-weight:bold; background:none; padding:0; margin:0.4em 0 0.2em;}
.wizard h2 {font-size:1.2em; font-weight:bold; background:none; padding:0; margin:0.4em 0 0.2em;}
.wizardStep {padding:1em 1em 1em 1em;}
.wizard .button {margin:0.5em 0 0; font-size:1.2em;}
.wizardFooter {padding:0.8em 0.4em 0.8em 0;}
.wizardFooter .status {padding:0 0.4em; margin-left:1em;}
.wizard .button {padding:0.1em 0.2em;}

#messageArea {position:fixed; top:2em; right:0; margin:0.5em; padding:0.5em; z-index:2000; _position:absolute;}
.messageToolbar {display:block; text-align:right; padding:0.2em;}
#messageArea a {text-decoration:underline;}

.tiddlerPopupButton {padding:0.2em;}
.popupTiddler {position: absolute; z-index:300; padding:1em; margin:0;}

.popup {position:absolute; z-index:300; font-size:.9em; padding:0; list-style:none; margin:0;}
.popup .popupMessage {padding:0.4em;}
.popup hr {display:block; height:1px; width:auto; padding:0; margin:0.2em 0;}
.popup li.disabled {padding:0.4em;}
.popup li a {display:block; padding:0.4em; font-weight:normal; cursor:pointer;}
.listBreak {font-size:1px; line-height:1px;}
.listBreak div {margin:2px 0;}

.tabset {padding:1em 0 0 0.5em;}
.tab {margin:0 0 0 0.25em; padding:2px;}
.tabContents {padding:0.5em;}
.tabContents ul, .tabContents ol {margin:0; padding:0;}
.txtMainTab .tabContents li {list-style:none;}
.tabContents li.listLink { margin-left:.75em;}

#contentWrapper {display:block;}
#splashScreen {display:none;}

#displayArea {margin:1em 17em 0 14em;}

.toolbar {text-align:right; font-size:.9em;}

.tiddler {padding:1em 1em 0;}

.missing .viewer,.missing .title {font-style:italic;}

.title {font-size:1.6em; font-weight:bold;}

.missing .subtitle {display:none;}
.subtitle {font-size:1.1em;}

.tiddler .button {padding:0.2em 0.4em;}

.tagging {margin:0.5em 0.5em 0.5em 0; float:left; display:none;}
.isTag .tagging {display:block;}
.tagged {margin:0.5em; float:right;}
.tagging, .tagged {font-size:0.9em; padding:0.25em;}
.tagging ul, .tagged ul {list-style:none; margin:0.25em; padding:0;}
.tagClear {clear:both;}

.footer {font-size:.9em;}
.footer li {display:inline;}

.annotation {padding:0.5em; margin:0.5em;}

* html .viewer pre {width:99%; padding:0 0 1em 0;}
.viewer {line-height:1.4em; padding-top:0.5em;}
.viewer .button {margin:0 0.25em; padding:0 0.25em;}
.viewer blockquote {line-height:1.5em; padding-left:0.8em;margin-left:2.5em;}
.viewer ul, .viewer ol {margin-left:0.5em; padding-left:1.5em;}

.viewer table, table.twtable {border-collapse:collapse; margin:0.8em 1.0em;}
.viewer th, .viewer td, .viewer tr,.viewer caption,.twtable th, .twtable td, .twtable tr,.twtable caption {padding:3px;}
table.listView {font-size:0.85em; margin:0.8em 1.0em;}
table.listView th, table.listView td, table.listView tr {padding:0px 3px 0px 3px;}

.viewer pre {padding:0.5em; margin-left:0.5em; font-size:1.2em; line-height:1.4em; overflow:auto;}
.viewer code {font-size:1.2em; line-height:1.4em;}

.editor {font-size:1.1em;}
.editor input, .editor textarea {display:block; width:100%; font:inherit;}
.editorFooter {padding:0.25em 0; font-size:.9em;}
.editorFooter .button {padding-top:0px; padding-bottom:0px;}

.fieldsetFix {border:0; padding:0; margin:1px 0px;}

.sparkline {line-height:1em;}
.sparktick {outline:0;}

.zoomer {font-size:1.1em; position:absolute; overflow:hidden;}
.zoomer div {padding:1em;}

* html #backstage {width:99%;}
* html #backstageArea {width:99%;}
#backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em;}
#backstageToolbar {position:relative;}
#backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em;}
#backstageButton {display:none; position:absolute; z-index:175; top:0; right:0;}
#backstageButton a {padding:0.1em 0.4em; margin:0.1em;}
#backstage {position:relative; width:100%; z-index:50;}
#backstagePanel {display:none; z-index:100; position:absolute; width:90%; margin-left:3em; padding:1em;}
.backstagePanelFooter {padding-top:0.2em; float:right;}
.backstagePanelFooter a {padding:0.2em 0.4em;}
#backstageCloak {display:none; z-index:20; position:absolute; width:100%; height:100px;}

.whenBackstage {display:none;}
.backstageVisible .whenBackstage {display:block;}
/*}}}*/
</pre>
</div>
<div title="StyleSheetLocale">
<pre>/***
StyleSheet for use when a translation requires any css style changes.
This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which need larger font sizes.
***/
/*{{{*/
body {font-size:0.8em;}
#sidebarOptions {font-size:1.05em;}
#sidebarOptions a {font-style:normal;}
#sidebarOptions .sliderPanel {font-size:0.95em;}
.subtitle {font-size:0.8em;}
.viewer table.listView {font-size:0.95em;}
/*}}}*/</pre>
</div>
<div title="StyleSheetPrint">
<pre>/*{{{*/
@media print {
#mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton, #backstageArea {display: none !important;}
#displayArea {margin: 1em 1em 0em;}
noscript {display:none;} /* Fixes a feature in Firefox 1.5.0.2 where print preview displays the noscript content */
}
/*}}}*/</pre>
</div>
<div title="PageTemplate">
<pre>&lt;!--{{{--&gt;
&lt;div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'&gt;
&lt;div class='headerShadow'&gt;
&lt;span class='siteTitle' refresh='content' tiddler='SiteTitle'&gt;&lt;/span&gt;&amp;nbsp;
&lt;span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;div class='headerForeground'&gt;
&lt;span class='siteTitle' refresh='content' tiddler='SiteTitle'&gt;&lt;/span&gt;&amp;nbsp;
&lt;span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id='mainMenu' refresh='content' tiddler='MainMenu'&gt;&lt;/div&gt;
&lt;div id='sidebar'&gt;
&lt;div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'&gt;&lt;/div&gt;
&lt;div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div id='displayArea'&gt;
&lt;div id='messageArea'&gt;&lt;/div&gt;
&lt;div id='tiddlerDisplay'&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;!--}}}--&gt;</pre>
</div>
<div title="ViewTemplate">
<pre>&lt;!--{{{--&gt;
&lt;div class='toolbar' macro='toolbar [[ToolbarCommands::ViewToolbar]]'&gt;&lt;/div&gt;
&lt;div class='title' macro='view title'&gt;&lt;/div&gt;
&lt;div class='subtitle'&gt;&lt;span macro='view modifier link'&gt;&lt;/span&gt;, &lt;span macro='view modified date'&gt;&lt;/span&gt; (&lt;span macro='message views.wikified.createdPrompt'&gt;&lt;/span&gt; &lt;span macro='view created date'&gt;&lt;/span&gt;)&lt;/div&gt;
&lt;div class='tagging' macro='tagging'&gt;&lt;/div&gt;
&lt;div class='tagged' macro='tags'&gt;&lt;/div&gt;
&lt;div class='viewer' macro='view text wikified'&gt;&lt;/div&gt;
&lt;div class='tagClear'&gt;&lt;/div&gt;
&lt;!--}}}--&gt;</pre>
</div>
<div title="EditTemplate">
<pre>&lt;!--{{{--&gt;
&lt;div class='toolbar' macro='toolbar [[ToolbarCommands::EditToolbar]]'&gt;&lt;/div&gt;
&lt;div class='title' macro='view title'&gt;&lt;/div&gt;
&lt;div class='editor' macro='edit title'&gt;&lt;/div&gt;
&lt;div macro='annotations'&gt;&lt;/div&gt;
&lt;div class='editor' macro='edit text'&gt;&lt;/div&gt;
&lt;div class='editor' macro='edit tags'&gt;&lt;/div&gt;&lt;div class='editorFooter'&gt;&lt;span macro='message views.editor.tagPrompt'&gt;&lt;/span&gt;&lt;span macro='tagChooser excludeLists'&gt;&lt;/span&gt;&lt;/div&gt;
&lt;!--}}}--&gt;</pre>
</div>
<div title="GettingStarted">
<pre>To get started with this blank [[TiddlyWiki]], you'll need to modify the following tiddlers:
* [[SiteTitle]] &amp; [[SiteSubtitle]]: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
* [[MainMenu]]: The menu (usually on the left)
* [[DefaultTiddlers]]: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
You'll also need to enter your username for signing your edits: &lt;&lt;option txtUserName&gt;&gt;</pre>
</div>
<div title="OptionsPanel">
<pre>These [[InterfaceOptions]] for customising [[TiddlyWiki]] are saved in your browser

Your username for signing your edits. Write it as a [[WikiWord]] (eg [[JoeBloggs]])

&lt;&lt;option txtUserName&gt;&gt;
&lt;&lt;option chkSaveBackups&gt;&gt; [[SaveBackups]]
&lt;&lt;option chkAutoSave&gt;&gt; [[AutoSave]]
&lt;&lt;option chkRegExpSearch&gt;&gt; [[RegExpSearch]]
&lt;&lt;option chkCaseSensitiveSearch&gt;&gt; [[CaseSensitiveSearch]]
&lt;&lt;option chkAnimate&gt;&gt; [[EnableAnimations]]

----
Also see [[AdvancedOptions]]</pre>
</div>
<div title="ImportTiddlers">
<pre>&lt;&lt;importTiddlers&gt;&gt;</pre>
</div>
</div>
<!--POST-SHADOWAREA-->
<div id="storeArea">
<div title="About" creator="YichunZhang" modifier="YichunZhang" created="201106210407" modified="201305080628" changecount="16">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

这个软件包主要由章亦春（[[agentzh|http://agentzh.org]]）维护。在 2011 年之前曾由[[淘宝网|http://www.taobao.com]]赞助，此后一直主要由美国的 [[CloudFlare 公司|http://www.cloudflare.com]] 提供支持。

因为大部分 Nginx 模块都是由本软件包的维护者开发，所以可以确保所有这些模块及其他组件可以很好地一起工作。

打包的各个软件组件版权属于各自的版权持有者。

本网站是一个 [[TiddlyWiki|http://www.tiddlywiki.com/]] 页面，其源代码被管理在 GitHub 上面：

http://github.com/agentzh/openresty.org

如果你希望编辑和改进这个网站的话，请随意建立这个仓库的分支，或者直接[[联系我们|ContactUs]]索取 git 提交权限。
</pre>
</div>
<div title="ArrayVarNginxModule" creator="YichunZhang" modifier="YichunZhang" created="201106210819" modified="201106210824" changecount="3">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

This [[Nginx]] module adds support for array variables to nginx config files.

Project page: https://github.com/agentzh/array-var-nginx-module</pre>
</div>
<div title="AuthRequestNginxModule" creator="YichunZhang" modifier="YichunZhang" created="201106210821" modified="201106210825" changecount="2">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

Introduction: http://www.ruby-forum.com/topic/205063

Project page: http://mdounin.ru/hg/ngx_http_auth_request_module/</pre>
</div>
<div title="Benchmark" creator="YichunZhang" modifier="YichunZhang" created="201106210550" modified="201106210734" changecount="5">
<pre>&lt;&lt;toolbar permalink&gt;&gt;
!~HelloWorld
Testing the performance of a ~HelloWorld server does not mean many things but it does tell us where the ceiling is.

The ~HelloWorld server based on OpenResty is described in the [[GettingStarted]] document.

Below is the result using the command {{{ab -c10 -n50000 http://localhost:8080/}}} on my ~ThinkPad T400 laptop with ngx_openresty 0.8.54.6:
{{{
Server Software:        ngx_openresty/0.8.54
Server Hostname:        localhost
Server Port:            8080

Document Path:          /
Document Length:        20 bytes

Concurrency Level:      10
Time taken for tests:   2.459 seconds
Complete requests:      50000
Failed requests:        0
Write errors:           0
Total transferred:      8550342 bytes
HTML transferred:       1000040 bytes
Requests per second:    20335.69 [#/sec] (mean)
Time per request:       0.492 [ms] (mean)
Time per request:       0.049 [ms] (mean, across all concurrent requests)
Transfer rate:          3396.04 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       8
Processing:     0    0   0.2      0       8
Waiting:        0    0   0.1      0       8
Total:          0    0   0.2      0       8

Percentage of the requests served within a certain time (ms)
  50%      0
  66%      0
  75%      0
  80%      0
  90%      1
  95%      1
  98%      1
  99%      1
 100%      8 (longest request)
}}}

So on my laptop, for a single nginx worker, we've got 20k+ r/s. For comparison, ~HelloWorld servers using nginx + php-fpm 5.2.8 gives 4k r/s, Erlang ~R14B2 raw gen_tcp server gives 8k r/s, and [[node.js|http://nodejs.org/] v0.4.8 yields 5.7k r/s.
</pre>
</div>
<div title="ChangeLog1000004" creator="YichunZhang" modifier="YichunZhang" created="201107081222" modified="201109020756" changecount="47">
<pre>&lt;&lt;toolbar permalink&gt;&gt;
! Stable Release 1.0.4.2 - 9 August 2011
This release is the same as  1.0.4.2rc13.

The following components are bundled by this release:
* ~LuaJIT-2.0.0-beta8
* array-var-nginx-module-0.03rc1
* auth-request-nginx-module-0.2
* drizzle-nginx-module-0.1.1rc3
* echo-nginx-module-0.37rc1
* encrypted-session-nginx-module-0.01
* form-input-nginx-module-0.07rc5
* headers-more-nginx-module-0.15
* iconv-nginx-module-0.10rc4
* lua-5.1.4
* memc-nginx-module-0.12
* nginx-1.0.4
* ngx_devel_kit-0.2.17
* ngx_lua-0.2.1rc4
* ngx_postgres-0.9rc1
* rds-json-nginx-module-0.12rc1
* redis2-nginx-module-0.07
* set-misc-nginx-module-0.22rc2
* srcache-nginx-module-0.12
* upstream-keepalive-nginx-module-0.3
* xss-nginx-module-0.03rc3
! Mainline Version 1.0.4.2rc13 - 8 August 2011
* now we bundle a Perl 5 script to serve as the {{{install}}} script for LuaJIT 2.0 on Solaris. now OpenResty builds successfully on Solaris 11 with LuaJIT enabled!
! Mainline Version 1.0.4.2rc12 - 8 August 2011
* bundled {{{gcc}}}'s {{{unwind-generic.h}}} for BSD because {{{unwind.h}}} is missing at least on ~FreeBSD.
! Mainline Version 1.0.4.2rc11 - 8 August 2011
* use absoluate paths in Makefile to prevent {{{-jN}}} errors when using bsdmake on ~FreeBSD.
! Mainline Version 1.0.4.2rc10 - 7 August 2011
* upgraded ngx_lua to v0.2.1rc4.
** worked-around the &quot;stack overflow&quot; issue while using luarocks.loader and disabling {{{lua_code_cache}}}, as described as [[github issue #27|https://github.com/chaoslawful/lua-nginx-module/issues/27]]. thanks Patrick Crosby.
! Mainline Version 1.0.4.2rc9 - 7 August 2011
* now we use {{{gmake}}} if it is available in {{{PATH}}} during {{{./configure}}}; also added the {{{--with-make=PATH}}} option to allow the user to specify a custom {{{make}}} utility.
! Mainline Version 1.0.4.2rc8 - 6 August 2011
* fixed a regression that we should use the {{{CC}}} variable instead of {{{HOST_CC}}} while passing the {{{--with-cc}}} option to the LuaJIT 2.0 build system. thanks @姜大炮 for reporting this issue.
! Mainline Version 1.0.4.2rc7 - 5 August 2011
*  upgraded ngx_lua to v0.2.1rc3.
** fixed the {{{zero size buf in output}}} alert while combining {{{lua_need_request_body on}}} + {{{access_by_lua/rewrite_by_lua}}} + {{{proxy_pass/fastcgi_pass}}}. thanks 万珣新.
! Mainline Version 1.0.4.2rc6 - 5 August 2011
* added the {{{--with-pg_config}}} option to the {{{./configure}}} script as per 罗翼's request.
! Mainline Version 1.0.4.2rc5 - 5 August 2011
* added the {{{--with-no-pool-patch}}} option to the {{{./configure}}} script, to allow enabling the no-pool patch for debugging memory issues with valgrind, for example.
! Mainline Version 1.0.4.2rc4 - 4 August 2011
* added the {{{--with-libpq=DIR}}} option to the {{{./configure}}} script. thanks 郭颖 for suggesting this.
! Mainline Version 1.0.4.2rc3 - 4 August 2011
* upgraded ngx_drizzle to v0.1.1rc3.
** fixed segmentation faults on Linux i386. thanks @魏世江 and @stefanli for reporting this issue.
! Mainline Version 1.0.4.2rc2 - 4 August 2011
* fixed a regression while specifying {{{--with-http_iconv_module}}} during {{{./configure}}}. thanks 冯新国 for reporting this issue.
! Mainline Version 1.0.4.2rc1 - 30 July 2011
* upgraded ngx_set_misc to 0.22rc2.
** added the set_misc_base32_padding directive.
! Stable Release 1.0.4.1 - 30 July 2011
This release is the same as  1.0.4.1rc6.

The following components are bundled by this release:
* ~LuaJIT-2.0.0-beta8
* array-var-nginx-module-0.03rc1
* auth-request-nginx-module-0.2
* drizzle-nginx-module-0.1.1rc2
* echo-nginx-module-0.37rc1
* encrypted-session-nginx-module-0.01
* form-input-nginx-module-0.07rc5
* headers-more-nginx-module-0.15
* iconv-nginx-module-0.10rc4
* lua-5.1.4
* memc-nginx-module-0.12
* nginx-1.0.4
* ngx_devel_kit-0.2.17
* ngx_lua-0.2.1rc2
* ngx_postgres-0.9rc1
* rds-json-nginx-module-0.12rc1
* redis2-nginx-module-0.07
* set-misc-nginx-module-0.22rc1
* srcache-nginx-module-0.12
* upstream-keepalive-nginx-module-0.3
* xss-nginx-module-0.03rc3
! Mainline Version 1.0.4.1rc6 - 28 July 2011
* fixed a regression in {{{./configure}}} when enabling luajit in 1.0.4.1rc5. thanks @Lance.
! Mainline Version 1.0.4.1rc5 - 26 July 2011
* upgraded ngx_iconv to 0.10rc3, ngx_form_input to 0.07rc5, ngx_array_var to 0.03rc1, and ngx_set_misc to 0.22rc1.
* now {{{--with-debug}}} option also enables the {{{gcc -g}}} compilation option for LuaJIT.
* disabled target stripping in LuaJIT.
! Mainline Version 1.0.4.1rc4 - 25 July 2011
* applied the official hotfix #1 patch for LuaJIT 2.0.0 beta8.
! Mainline Version 1.0.4.1rc3 - 23 July 2011
* now the {{{--with-cc}}} option of {{{./configure}}} also controls the C compiler used by Lua and LuaJIT. thanks @姜大炮 for reporting the issue.
! Mainline Version 1.0.4.1rc2 - 23 July 2011
* upgraded ngx_lua to v0.2.1rc2 and ngx_redis2 to v0.07.
! Mainline Version 1.0.4.1rc1 - 14 July 2011
* upgraded ngx_rds_json to v0.12rc1, ngx_drizzle to v0.1.1rc2, ngx_lua to v0.2.1rc1, ngx_postgres to v0.9rc1, ngx_redis2 to v0.07rc6.
* now we no longer enable {{{gcc -O2}}} by default, because it's bad for debugging issues.
* fixed linking issues when doing {{{./configure --with-cc-opt=-fast}}} on Mac OS X. thanks @姜大炮.
! Stable Release 1.0.4.0 - 12 July 2011
This release is the same as  1.0.4.0rc5.

The following components are bundled by this release:
* ~LuaJIT-2.0.0-beta8
* array-var-nginx-module-0.02
* auth-request-nginx-module-0.2
* drizzle-nginx-module-0.1.1rc1
* echo-nginx-module-0.37rc1
* encrypted-session-nginx-module-0.01
* form-input-nginx-module-0.07rc4
* headers-more-nginx-module-0.15
* iconv-nginx-module-0.10rc3
* lua-5.1.4
* memc-nginx-module-0.12
* nginx-1.0.4
* ngx_devel_kit-0.2.17
* ngx_lua-0.2.0
* ngx_postgres-0.8
* rds-json-nginx-module-0.11
* redis2-nginx-module-0.07rc5
* set-misc-nginx-module-0.21
* srcache-nginx-module-0.12
* upstream-keepalive-nginx-module-0.3
* xss-nginx-module-0.03rc3
! Mainline Version 1.0.4.0rc5 - 12 July 2011
* upgraded ngx_drizzle to v0.1.1rc1.
! Mainline Version 1.0.4.0rc4 - 11 July 2011
* upgraded ngx_echo to v0.37rc1.
* we no longer require ~ExtUtils::~MakeMaker and Config because they're not standard packages and are missing on a default ~CentOS 6 build. thanks Lance for reporting it.
! Mainline Version 1.0.4.0rc2 - 11 July 2011
* upgraded ngx_srcache to v0.12.
! Mainline Version 1.0.4.0rc1 - 8 July 2011
* based on ngx_openresty 0.8.54.9, but with nginx core upgraded to nginx 1.0.4.

See [[ChangeLog8054]] for change log for ngx_openresty 0.8.54.x.</pre>
</div>
<div title="ChangeLog1000005" creator="YichunZhang" modifier="YichunZhang" created="201108090717" modified="201109040354" changecount="38">
<pre>&lt;&lt;toolbar permalink&gt;&gt;
! Stable Release 1.0.5.1 - 4 September 2011
This release is almost the same as 1.0.5.1rc14, but upgraded LuaNginxModule to v0.3.0.

The following components are bundled by this release:
* ~LuaJIT-2.0.0-beta8
* array-var-nginx-module-0.03rc1
* auth-request-nginx-module-0.2
* drizzle-nginx-module-0.1.1rc4
* echo-nginx-module-0.37rc2
* encrypted-session-nginx-module-0.01
* form-input-nginx-module-0.07rc5
* headers-more-nginx-module-0.15
* iconv-nginx-module-0.10rc4
* lua-5.1.4
* lua-cjson-1.0.2
* lua-rds-parser-0.03
* lua-redis-parser-0.09rc5
* memc-nginx-module-0.12
* nginx-1.0.5
* ngx_devel_kit-0.2.17
* ngx_lua-0.3.0
* ngx_postgres-0.9rc1
* rds-csv-nginx-module-0.02
* rds-json-nginx-module-0.12rc2
* redis2-nginx-module-0.07
* set-misc-nginx-module-0.22rc2
* srcache-nginx-module-0.12
* upstream-keepalive-nginx-module-0.3
* xss-nginx-module-0.03rc3
! Mainline Version 1.0.5.1rc14 - 2 September 2011
* upgraded LuaNginxModule to v0.2.1rc22.
** fixed an issue with {{{header_filter_by_lua}}} directive: it was not supported in scopes other than the location scope.
! Mainline Version 1.0.5.1rc13 - 1 September 2011
* upgraded LuaNginxModule to 0.2.1rc21.
** added the {{{header_filter_by_lua}}} and {{{header_filter_by_lua_file}}} directives: http://wiki.nginx.org/HttpLuaModule#header_filter_by_lua
** fixed issues with HTTP 1.0 HEAD requests.
* upgraded EchoNginxModule to 0.37rc2
** fixed issues when errors happen in a downstream output filter.
** fixed issues with HTTP HEAD requests.
! Mainline Version 1.0.5.1rc12 - 31 August 2011
* now we bundle LuaRdsParserLibrary and enable it by default.
* added the {{{--without-lua_rds_parser}}} option to disable the LuaRdsParserLibrary bundled.
* now we bundle RdsCsvNginxModule and enable it by default.
* added the {{{--without-http_rds_csv_module}}} option to disable RdsCsvNginxModule.
! Mainline Version 1.0.5.1rc11 - 30 August 2011
* upgraded RdsJsonNginxModule to v0.12rc2
** fixed a bug in compact JSON mode: the column name in the resultset might not be escaped for JSON encoding.
! Mainline Version 1.0.5.1rc10 - 29 August 2011
* upgraded LuaRedisParserLibrary to 0.09rc5.
** added wiki documentation page for this Lua library: http://wiki.nginx.org/LuaRedisParser
** added the {{{typename}}} method for converting the numeric type values returned by {{{parse_reply}}} and {{{parse_replies}}} to textual type names: http://wiki.nginx.org/LuaRedisParser#typename
! Mainline Version 1.0.5.1rc9 - 27 August 2011
* upgraded LuaNginxModule to v0.2.1rc19.
** implemented the {{{o}}} regex option (i.e., the compile-once flag as Perl's {{{/o}}} modifier) for all the {{{ngx.re.*}}} API.
** added new directive {{{lua_regex_cache_max_entries}}} to control the upper limit of the worker-process-level compiled-regex cache enabled by the {{{o}}} regex option: http://wiki.nginx.org/HttpLuaModule#lua_regex_cache_max_entries
** now we add {{{ngx}}} and {{{ndk}}} table into {{{package.loaded}}} such that the user can write {{{local ngx = require 'ngx'}}} and {{{local ndk = require 'ndk'}}}. thanks @Lance.
! Mainline Version 1.0.5.1rc8 - 26 August 2011
* upgraded LuaNginxModule to v0.2.1rc18.
** fixed a bug in the {{{ngx.re.*}}} regex API that look-behind assertions in PCRE regexes did not work properly.
! Mainline Version 1.0.5.1rc7 - 26 August 2011
* upgraded LuaNginxModule to v0.2.1rc17.
** now we enable {{{ngx.re.*}}} regex API in {{{set_by_lua*}}} too.
! Mainline Version 1.0.5.1rc6 - 25 August 2011
* upgraded LuaNginxModule to v0.2.1rc16.
** fixed github issue #52: compile error with nginx 1.0.5 on Ubuntu natty.
** fixed issues found by gcc 4.6 {{{-Wunused-but-set-variable}}} warnings.
! Mainline Version 1.0.5.1rc5 - 24 August 2011
* upgraded LuaNginxModule to v0.2.1rc15.
** implemented the {{{ngx.re.gsub()}}} regex API for Lua: http://wiki.nginx.org/NginxHttpLuaModule#ngx.re.gsub
! Mainline Version 1.0.5.1rc4 - 24 August 2011
* upgraded LuaNginxModule to v0.2.1rc14.
** added support for the optional {{{ctx}}} argument to {{{ngx.re.match}}}.
! Mainline Version 1.0.5.1rc3 - 24 August 2011
* upgraded LuaNginxModule to v0.2.1rc13.
** implemented the {{{ngx.re.sub()}}} regex API for Lua: http://wiki.nginx.org/NginxHttpLuaModule#ngx.re.sub
** added support for anchored match modifier {{{a}}} to {{{ngx.re.match}}}, {{{ngx.re.gmatch}}}, and {{{ngx.re.sub}}}.
* upgraded DrizzleNginxModule to v0.1.1rc4
** added new wiki documentation page: http://wiki.nginx.org/NginxHttpDrizzleModule
** documented the {{{$drizzle_thread_id}}} variable.
** added lots of debug outputs (enabled by the {{{--with-debug}}} option while building Nginx or OpenResty), inspired by github issue #10.
! Mainline Version 1.0.5.1rc2 - 18 August 2011
* upgraded LuaNginxModule to v0.2.1rc12.
** implemented the {{{ngx.re.gmatch()}}} regex API for Lua: http://wiki.nginx.org/NginxHttpLuaModule#ngx.re.gmatch
! Mainline Version 1.0.5.1rc1 - 17 August 2011
* upgraded LuaNginxModule to v0.2.1rc11.
** now {{{ngx.ctx = {...} }}} assignment is supported.
** made setting {{{ngx.header.HEADER}}} after sending out response headers throw out a Lua exception to help debugging issues like github issue #49. thanks Bill Donahue (ikhoyo).
** implemented the {{{ngx.re.match()}}} regex API for Lua: http://wiki.nginx.org/NginxHttpLuaModule#ngx.re.match
! Stable Release 1.0.5.0 - 16 August 2011
This release is the same as 1.0.5.0rc7.

The following components are bundled by this release:
* ~LuaJIT-2.0.0-beta8
* array-var-nginx-module-0.03rc1
* auth-request-nginx-module-0.2
* drizzle-nginx-module-0.1.1rc3
* echo-nginx-module-0.37rc1
* encrypted-session-nginx-module-0.01
* form-input-nginx-module-0.07rc5
* headers-more-nginx-module-0.15
* iconv-nginx-module-0.10rc4
* lua-5.1.4
* lua-cjson-1.0.2
* lua-redis-parser-0.09rc4
* memc-nginx-module-0.12
* nginx-1.0.5
* ngx_devel_kit-0.2.17
* ngx_lua-0.2.1rc9
* ngx_postgres-0.9rc1
* rds-json-nginx-module-0.12rc1
* redis2-nginx-module-0.07
* set-misc-nginx-module-0.22rc2
* srcache-nginx-module-0.12
* upstream-keepalive-nginx-module-0.3
* xss-nginx-module-0.03rc3
! Mainline Version 1.0.5.0rc7 - 13 August 2011
* upgraded LuaNginxModule to v0.2.1rc9.
** implemented the special {{{ngx.ctx}}} Lua table for user programmers to store per-request Lua context data for their applications. thanks 欧远宁 for suggesting this feature.
! Mainline Version 1.0.5.0rc6 - 12 August 2011
* upgraded LuaNginxModule to v0.2.1rc8.
** now {{{ngx.print}}} and {{{ngx.say}}} allow (nested) array-like table arguments. the array elements in them will be sent piece by piece. this will avoid string concatenation for templating engines like [[ltp|http://www.savarese.com/software/ltp/]].
! Mainline Version 1.0.5.0rc5 - 12 August 2011
* upgraded LuaNginxModule to v0.2.1rc7.
** implemented the {{{ngx.req.get_post_args()}}} method for fetching urlencoded POST query arguments from within Lua.
** renamed {{{ngx.req.get_query_args}}} to {{{ngx.req.get_uri_args}}}. the former is now deprecated.
** fixed a bug in {{{ngx.req.get_uri_args}}}: it could not be used more than once in a single request.
! Mainline Version 1.0.5.0rc4 - 12 August 2011
* upgraded LuaNginxModule to v0.2.1rc5.
** implemented the {{{ngx.req.get_query_args()}}} method to fetch parsed URL query arguments from within Lua. thanks Bertrand Mansion (golgote).
** now we allow Lua boolean and {{{nil}}} values in arguments to {{{ngx.say()}}}, {{{ngx.print()}}}, {{{ngx.log()}}} and {{{print()}}}.
! Mainline Version 1.0.5.0rc3 - 11 August 2011
* now we bundle the LuaRedisParserLibrary with us and it is enabled by default. tested on Linux i386, Linux x86_64, Mac OS X, ~FreeBSD 8.2 i386, and Solaris 11.
* added the new option {{{--without-lua_redis_parser}}} to the {{{./configure}}} script.
! Mainline Version 1.0.5.0rc2 - 10 August 2011
* now we bundle the LuaCjsonLibrary with us and it is enabled by default. tested on Linux i386, Linux x86_64, Mac OS X, ~FreeBSD 8.2 i386, and Solaris 11.
* added the new option {{{--without-lua_cjson}}} to the {{{./configure}}} script.
* added {{{&lt;prefix&gt;/lualib}}} to the default {{{path}}} and {{{cpath}}} settings of the ngx_lua's Lua VM.
! Mainline Version 1.0.5.0rc1 - 9 August 2011
* based on ngx_openresty 1.0.4.2, but with nginx core upgraded to nginx 1.0.5.

See [[ChangeLog1000004]] for change log for ngx_openresty 1.0.4.x.
</pre>
</div>
<div title="ChangeLog1000006" creator="YichunZhang" modifier="YichunZhang" created="201109040816" modified="201110070457" changecount="24">
<pre>&lt;&lt;toolbar permalink&gt;&gt;
! Stable Release 1.0.6.22 - 7 October 2011
This release is exactly the same as the devel release 1.0.6.21.

The following components are bundled by this release:
* ~LuaJIT-2.0.0-beta8
* array-var-nginx-module-0.03rc1
* auth-request-nginx-module-0.2
* drizzle-nginx-module-0.1.1
* echo-nginx-module-0.37rc4
* encrypted-session-nginx-module-0.01
* form-input-nginx-module-0.07rc5
* headers-more-nginx-module-0.16rc2
* iconv-nginx-module-0.10rc4
* lua-5.1.4
* lua-cjson-1.0.3
* lua-rds-parser-0.03
* lua-redis-parser-0.09rc5
* memc-nginx-module-0.12
* nginx-1.0.6
* ngx_devel_kit-0.2.17
* ngx_lua-0.3.1rc8
* ngx_postgres-0.9rc1
* rds-csv-nginx-module-0.03
* rds-json-nginx-module-0.12rc5
* redis2-nginx-module-0.07
* set-misc-nginx-module-0.22rc2
* srcache-nginx-module-0.12
* upstream-keepalive-nginx-module-0.3
* xss-nginx-module-0.03rc3
! Mainline Version 1.0.6.21 - 23 September 2011
* added new option {{{-jN}}} (e.g., {{{-j8}}}, {{{-j10}}}, and etc.) to OpenResty's {{{./configure}}} script to allow parallel build of the dependencies like LuaJIT; thanks @Lance.
! Mainline Version 1.0.6.19 - 23 September 2011
* upgraded LuaNginxModule to v0.3.1rc8.
** exposes the {{{CRC-32}}} API of the Nginx core to the Lua land, in the form of the {{{ngx.crc32_short}}} and {{{ngx.crc32_long}}} methods. thanks @Lance.
! Mainline Version 1.0.6.17 - 23 September 2011
* upgraded LuaNginxModule to v0.3.1rc7.
** now {{{ngx.exec()}}} supports lua table as the second {{{args}}} argument value. thanks sexybabes.
** implemented the {{{ngx.headers_sent}}} API to check if response headers are sent (by ngx_lua). thanks @hugozhu.
! Mainline Version 1.0.6.15 - 22 September 2011
* upgraded LuaNginxModule to v0.3.1rc5.
** now we also return the {{{Last-Modified}}} header (if any) for the subrequest response object. thanks @cyberty and sexybabes.
! Mainline Version 1.0.6.13 - 21 September 2011
* upgraded LuaNginxModule to v0.3.1rc4.
** fixed an issue in {{{ngx.redirect}}}, {{{ngx.exit}}}, and {{{ngx.exec}}}: these function calls would be intercepted by Lua {{{pcall}}}/{{{xpcall}}} because they used lua exceptions; now they use lua yield just as {{{ngx.location.capture}}}. thanks @hugozhu for reporting this.
! Stable Release 1.0.6.12 - 21 September 2011
This release is exactly the same as the devel release 1.0.6.11.

The following components are bundled by this release:
* ~LuaJIT-2.0.0-beta8
* array-var-nginx-module-0.03rc1
* auth-request-nginx-module-0.2
* drizzle-nginx-module-0.1.1
* echo-nginx-module-0.37rc4
* encrypted-session-nginx-module-0.01
* form-input-nginx-module-0.07rc5
* headers-more-nginx-module-0.16rc2
* iconv-nginx-module-0.10rc4
* lua-5.1.4
* lua-cjson-1.0.3
* lua-rds-parser-0.03
* lua-redis-parser-0.09rc5
* memc-nginx-module-0.12
* nginx-1.0.6
* ngx_devel_kit-0.2.17
* ngx_lua-0.3.1rc3
* ngx_postgres-0.9rc1
* rds-csv-nginx-module-0.03
* rds-json-nginx-module-0.12rc5
* redis2-nginx-module-0.07
* set-misc-nginx-module-0.22rc2
* srcache-nginx-module-0.12
* upstream-keepalive-nginx-module-0.3
* xss-nginx-module-0.03rc3
! Mainline Version 1.0.6.11 - 20 September 2011
* upgraded RdsJsonNginxModule to v0.12rc4.
** made {{{rds_json_ret}}} honor {{{rds_json_success_property}}} and {{{rds_json_user_property}}}. thanks Liseen Wan (万珣新)
** only register our output filters when the {{{rds_json}}} directive is actually used in {{{nginx.conf}}}.
* upgraded RdsCsvNginxModule to v0.03.
** only register our output filters when the {{{rds_csv}}} directive is actually used in {{{nginx.conf}}}.
! Mainline Version 1.0.6.9 - 19 September 2011
* upgraded LuaCjsonLibrary to v1.0.3.
! Mainline Version 1.0.6.7 - 18 September 2011
* added new options {{{--with-luajit=PATH}}} and {{{--with-lua51=PATH}}} to the {{{./configure}}} script. thanks ~NginxUser.
* upgraded DrizzleNginxModule to v0.1.1.
! Mainline Version 1.0.6.5 - 15 September 2011
* upgraded RdsJsonNginxModule to 0.12rc3.
** implemented new directive {{{rds_json_root}}}.
** implemented new directive {{{rds_json_success_property}}}.
** implemented new directive {{{rds_json_user_property}}}.
! Mainline Version 1.0.6.3 - 14 September 2011
* upgraded LuaNginxModule to 0.3.1rc3.
** implemented and documented the API for reading response headers from within Lua: {{{value = ngx.header.HEADER}}}.
** fixed a bug when setting a multi-value response header to a single value (via writing to {{{ngx.header.HEADER}}}): the single value will be repeated on each old value.
* upgraded EchoNginxModule to 0.37rc4.
** fixed a bug in {{{echo_after_body}}}: when network is not perfect, data truncation might occur. we should have taken into account {{{NGX_AGAIN}}} returned by the downstream output filters. thanks Sparsh Gupta.
* upgraded HeadersMoreNginxModule to v0.16rc2.
** fixed a bug when setting a multi-value response header to a single value: the single value will be repeated on each old value.
* applied the patch from Maxim Dounin to fix a bug in the standard ngx_gzip module when dealing with empty flush buffers: http://mailman.nginx.org/pipermail/nginx-devel/2011-February/000730.html
* updated the no-pool-patch to eliminate the {{{-Wset-but-not-used}}} warnings issued by gcc 4.6.0.
! Mainline Version 1.0.6.1 - 8 September 2011
* upgraded LuaNginxModule to 0.3.1rc1.
** fixed a bug when the both the main request and the subrequest are POST requests with a body: we should not forward the main request's {{{Content-Length}}} headers to the user subrequests. thanks 朱峰.
! Mainline Version 1.0.6.0rc2 - 4 September 2011
* upgraded HeadersMoreNginxModule to 0.16rc1.
** fixed on-demand hander/filter registration trick for {{{HUP}}} signal restarts.
** added some debugging outputs that can be enabled by the {{{--with-debug}}} option when building Nginx or OpenResty.
! Mainline Version 1.0.6.0rc1 - 4 September 2011
* based on ngx_openresty 1.0.5.1, but with nginx core upgraded to nginx 1.0.6.

See [[ChangeLog1000005]] for change log for ngx_openresty 1.0.5.x.
</pre>
</div>
<div title="ChangeLog1000008" creator="YichunZhang" modifier="YichunZhang" created="201110100846" modified="201111030908" changecount="46">
<pre>&lt;&lt;toolbar permalink&gt;&gt;
! Stable Release 1.0.8.26 - 3 November 2011
* now we require {{{gmake}}} ({{{Gnu make}}}) for {{{*BSD}}} systems even if LuaJIT is not enabled. thanks [[@lhmwzy|http://weibo.com/lhmwzy]].
* upgraded the official [[hotfix patch #4|http://www.lua.org/ftp/patch-lua-5.1.4-4]] for the standard Lua 5.1.4 interpreter.
Components bundled in this release:
* ~LuaJIT-2.0.0-beta8
* array-var-nginx-module-0.03rc1
* auth-request-nginx-module-0.2
* drizzle-nginx-module-0.1.2rc2
* echo-nginx-module-0.37rc7
* encrypted-session-nginx-module-0.01
* form-input-nginx-module-0.07rc5
* headers-more-nginx-module-0.16rc3
* iconv-nginx-module-0.10rc5
* lua-5.1.4
* lua-cjson-1.0.3
* lua-rds-parser-0.03
* lua-redis-parser-0.09rc5
* memc-nginx-module-0.13rc1
* nginx-1.0.8
* ngx_devel_kit-0.2.17
* ngx_lua-0.3.1rc23
* ngx_postgres-0.9rc1
* rds-csv-nginx-module-0.04
* rds-json-nginx-module-0.12rc6
* redis2-nginx-module-0.08rc1
* set-misc-nginx-module-0.22rc3
* srcache-nginx-module-0.13rc2
* upstream-keepalive-nginx-module-0.3
* xss-nginx-module-0.03rc3
! Mainline Version 1.0.8.25 - 27 October 2011
* upgraded LuaNginxModule to 0.3.1rc23.
** bugfix: {{{ndk.set_var.DIRECTIVE}}} had a memory issue and might pass empty argument values to the directive being called. thanks dannynoonan.
! Mainline Version 1.0.8.23 - 27 October 2011
* upgraded LuaNginxModule to 0.3.1rc22.
** feature: implemented new methods {{{add}}}, {{{replace}}}, {{{incr}}}, and {{{delete}}} for {{{ngx.shared.DICT}}}.
** bugfix: made the {{{set}}} method of {{{ngx.shared.DICT}}} return 3 values: {{{success}}}, {{{err}}}, and {{{forcible}}}.
** bugfix: fixed spots of {{{-Werror=unused-but-set-variable}}} warning issued by gcc 4.6.0.
! Mainline Version 1.0.8.21 - 26 October 2011
* upgraded LuaNginxModule to 0.3.1rc21.
** feature: added new directive {{{lua_shared_dict}}}: http://wiki.nginx.org/HttpLuaModule#lua_shared_dict
** feature: added Lua API for the shm-based dictionary: http://wiki.nginx.org/HttpLuaModule#ngx.shared.DICT
* upgraded EchoNginxModule to 0.37rc7.
** bugfix: fixed a memory issue in both {{{echo_sleep}}} and {{{echo_blocking_sleep}}}: we should not pass {{{ngx_str_t}}} strings to {{{atof()}}} which expects C strings.
! Mainline Version 1.0.8.19 - 24 October 2011
* upgraded LuaNginxModule to 0.3.1rc20.
** bugfix: no longer free request body buffers that are not allocated by ourselves.
** bugfix: now we allow setting {{{ngx.var.VARIABLE}}} to {{{nil}}}.
! Mainline Version 1.0.8.17 - 22 October 2011
* upgraded LuaNginxModule to 0.3.1rc19.
** feature: now we apply the patch to the nginx core so as to allow main request body modifications: https://github.com/agentzh/ngx_openresty/blob/master/patches/nginx-1.0.8-allow_request_body_updating.patch
** feature: added new Lua API {{{ngx.req.set_body_file()}}}: http://wiki.nginx.org/HttpLuaModule#ngx.req.set_body_file
** feature: added new Lua API {{{ngx.req.set_body_data()}}}: http://wiki.nginx.org/HttpLuaModule#ngx.req.set_body_data
** bugfix: {{{lua_need_request_body}}} should not skip requests with methods other than {{{POST}}} and {{{PUT}}}. thanks Nginx User.
! Mainline Version 1.0.8.15 - 19 October 2011
* upgraded LuaNginxModule to 0.3.1rc17.
** feature: added new Lua functions {{{ngx.req.read_body()}}}, {{{ngx.req.discard_body()}}}, {{{ngx.req.get_body_data()}}}, and {{{ngx.req.get_body_file()}}}. see the docs here: http://wiki.nginx.org/HttpLuaModule#ngx.req.read_body
** bugfix: fixed hanging issues when using {{{ngx.exec()}}} within {{{rewrite_by_lua}}} and {{{access_by_lua}}}. thanks Nginx User for reporting it.
! Mainline Version 1.0.8.13 - 16 October 2011
* upgraded LuaNginxModule to 0.3.1rc16.
** fixed compilation failures when {{{--with-debug}}} is turned off.
** now we prohibit use of {{{true}}} jump argument in {{{ngx.req.set_uri()}}} in contexts other than {{{rewrite_by_lua}}} and {{{rewrite_by_lua_file}}}. a lua exception will be thrown if the context is incorrect.
! Mainline Version 1.0.8.11 - 16 October 2011
* upgraded LuaNginxModule to 0.3.1rc14.
** now we change the {{{ngx.req.set_uri()}}} API a bit by changing the optional argument {{{break_cycle}}} to {{{jump}}}. so now it will not trigger location jump by default (because the {{{jump}}} argument is false by default) and in case {{{jump}}} is given true, the function will re-search locations and jump to the new location and never return.
! Mainline Version 1.0.8.9 - 16 October 2011
* upgraded LuaNginxModule to 0.3.1rc13.
** now we implemented {{{ngx.req.set_uri()}}} and {{{ngx.req.set_uri_args()}}} to emulate {{{ngx_rewrite}}}'s {{{rewrite}}} directive (without {{{redirect}}} or {{{permanent}}} modifiers). thanks Vladimir Protasov (utros) and Nginx User.
** now we skip rewrite phase Lua handlers altogether if {{{ngx_rewrite}}}'s {{{rewrite}}} directive issue a location re-lookup by changing ~URIs (but not including rewrite ... break). thanks Nginx User.
** added constant {{{ngx.HTTP_METHOD_NOT_IMPLEMENTED}}} (501). thanks Nginx User.
! Mainline Version 1.0.8.7 - 15 October 2011
* upgraded SrcacheNginxModule to 0.13rc2.
** bugfix: we now only cache 200, 301, and 302 responses by default.
** feature: implemented new directive {{{srcache_store_statuses}}} to allow the user to specify the response status code list that is to be stored into the cache.
! Mainline Version 1.0.8.5 - 13 October 2011
* upgraded LuaNginxModule to 0.3.1rc11.
** bugfix: now we explicitly clear all the modules' contexts before dump to named location with {{{ngx.exec}}}.
* upgraded EchoNginxModule to 0.37rc6.
** bugfix: now we explicitly clear all the modules' contexts before dump to named location with {{{echo_exec}}}.
** bugfix: bugfix: {{{echo_exec}}} may hang when running after {{{echo_sleep}}} (or other I/O interruption calls): we should have called {{{ngx_http_finalize_request}}} on {{{NGX_DONE}}} to decrement {{{r-&gt;main-&gt;count}}} anyway.
* applied the patch to the Nginx core that always clears all modules' contexts in {{{ngx_http_named_location}}}.
! Mainline Version 1.0.8.3 - 13 October 2011
* upgraded LuaNginxModule to 0.3.1rc10.
** bugfix: calling {{{ngx.exec()}}} to jump to a named location did not clear the context object of LuaNginxModule properly and might cause evil problems. thanks Nginx User.
* upgraded IconvNginxModule to 0.10rc5.
** bugfix: fixed {{{-Wset-but-not-used}}} warnings issued by gcc 4.6.0. thanks 支家乐 (Calio).
! Mainline Version 1.0.8.1 - 10 October 2011
* upgraded EchoNginxModule to 0.37rc5.
** bugfix: now we properly set the {{{Content-Length}}} request header for subrequests.
* upgraded LuaNginxModule to 0.3.1rc9.
** feature: now for HTTP 1.0 requests, we disable the automatic full buffering mode if the user sets the {{{Content-Length}}} response header before sending out the headers. this allows streaming output for HTTP 1.0 requests if the content length can be calculated beforehand. thanks 李子义.
** bugfix: now we properly support setting the {{{Cache-Control}}} response header via the {{{ngx.header.HEADER}}} interface.
** bugfix: no longer set header hash to 1. use the {{{ngx_hash_key_lc}}} instead.
* upgraded HeadersMoreNginxModule to 0.16rc3.
** bugfix: we should set header hash using {{{ngx_hash_key_lc}}}, not simply to 1.
** bugfix: fixed setting {{{Cache-Control}}} response headers. we should properly prepare the {{{r-&gt;cache_control}}} array as well.
* upgraded SrcacheNginxModule to 0.13rc1.
** implemented response status line and general response header caching and added new directives {{{srcache_store_hide_header}}} and {{{srcache_store_pass_header}}} to control which headers to cache and which not.
** added new directive {{{srcache_response_cache_control}}} to control whether honor response headers {{{Cache-Control}}} and {{{Expires}}}, default {{{on}}}.
** we disable {{{srcache_store}}} automatically by default when {{{Cache-Control: max-age=0}}} and {{{Expires: &lt;date no more recently than now&gt;}}} are seen.
** implemented builtin nginx variable {{{$srcache_expire}}} for automatic expiration time calculation based on response headers {{{Cache-Control}}} ({{{max-age}}}) and {{{Expires}}}; also added new directives {{{srcache_max_expire}}} and {{{srcache_default_expire}}}.
** implemented the {{{srcache_store_no_cache}}} directive; now by default, we do not store responses with the header {{{Cache-Control: no-cache}}} into the cache.
** implemented {{{the srcache_store_no_store directive}}} (default {{{off}}}). Now by default, responses with the header {{{Cache-Control: no-store}}} will not be stored into the cache.
** implemented the {{{srcache_store_private}}} directive to control whether to store responses with the header {{{Cache-Control: private}}}.
** implemented the {{{srcache_request_cache_control}}} directive to allow request headers {{{Cache-Control: no-cache}}} or {{{Pragma: no-cache}}} to force bypassing cache lookup. it also honors the request header {{{Cache-Control: no-store}}}. this directive is turned off by default.
** now we check response header {{{Content-Encoding}}} by default and a non-empty header value will {{{skip srcache_store}}}; also introduced a new directive named {{{srcache_ignore_content_encoding}}} to ignore this response header.
** implemented the {{{srcache_methods}}} directive to specify request methods that are cacheable, by default, only {{{GET}}} and {{{HEAD}}} are cacheable.
** bugfix: we no longer set header hash to 1; we use {{{ngx_hash_key_lc}}} instead.
** bugfix: when we skip {{{srcache_fetch}}} by means of {{{srcache_fetch_skip}}}, we should not automatically skip {{{srcache_store}}}.
** bugfix: now we ignore the {{{Content-Length}}} header (if any) of the main request for the subrequests.
** bugfix: there might be a segfault when failing to allocate memory in {{{ngx_http_srcache_add_copy_chain}}}. thanks Shaun savage.
* upgraded RdsJsonNginxModule to 0.12rc6.
** bugfix: fixed compatibility with nginx 1.1.4+.
* upgraded RdsCsvNginxModule to 0.04.
** bugfix: fixed compatibility issues with nginx 1.1.4+.
** optimization: now we only register our filters when {{{rds_csv}}} is actually used in {{{nginx.conf}}}.
* upgraded Redis2NginxModule to 0.08rc1.
** bugfix: fixed compatibility with nginx 1.1.4+.
* upgraded DrizzleNginxModule to 0.1.2rc2.
** bugfix: fixed compatibility with nginx 1.1.4+
* upgraded MemcNginxModule to 0.13rc1.
** bugfix: fixed compatibility with nginx 1.1.4+.
* upgraded SetMiscNginxModule to v0.22rc3.
** minor code cleanup.
* applied the patch for the variable-header-ignore-no-hash issue. see http://forum.nginx.org/read.php?29,216062 for details.
* based on OpenResty 1.0.6.22 and upgraded the Nginx core to 1.0.8.

See [[ChangeLog1000006]] for change log for ngx_openresty 1.0.6.x.
</pre>
</div>
<div title="ChangeLog1000009" creator="YichunZhang" modifier="YichunZhang" created="201111080101" modified="201111080120" changecount="9">
<pre>&lt;&lt;toolbar permalink&gt;&gt;
! Mainline Version 1.0.9.1 - 8 November 2011
* upgraded the Nginx core to 1.0.9.
* applied the [[epoll_check_stale_wev patch|http://mailman.nginx.org/pipermail/nginx-devel/2011-November/001408.html]] to the Nginx 1.0.9 core. thanks [[@晓旭XX|http://weibo.com/u/1878897190]].
* upgraded LuaNginxModule to 0.3.1rc26.
** feature: added the {{{ctx}}} option to [[ngx.location.capture|http://wiki.nginx.org/HttpLuaModule#ngx.location.capture]]: you can now specify a custom Lua table to pass to the subrequest as its [[ngx.ctx|http://wiki.nginx.org/HttpLuaModule#ngx.ctx]]. thanks [[@hugozhu|http://weibo.com/hugozhu]].
** bugfix: fixed compatibility with nginx 0.8.54. thanks [[@0579虾|http://weibo.com/shrimp0597]].
* upgraded PostgresNginxModule to 0.9rc2
** bugfix: now we log an error message when the {{{postgres_pass}}} target is not found at all and returns 500 in this case instead of returning empty response.
**  bugfix: we should no longer return {{{NGX_AGAIN}}} when the re-polling returns IO WAIT in case of the &quot;connection made&quot; state.
** feature: added some debugging outputs which be enabled by passing the {{{--with-debug}}} option while building Nginx or OpenResty.
** bugfix: fixed compatibility issues with Nginx 1.1.4+: {{{ngx_chain_update_chains}}} now requires a pool argument.
* upgraded LuaRdsParserLibrary to 0.04.
** bugfix: fixed a serious memory leak reported by bearnard.
* upgraded XssNginxModule to 0.03rc5.
** bugfix: the callback argument value parser did not accept ~JavaScript identifier names started with underscores. thanks Sam Mulube.
See [[ChangeLog1000008]] for change log for ngx_openresty 1.0.8.x.</pre>
</div>
<div title="ChangeLog1000010" creator="YichunZhang" modifier="YichunZhang" created="201205131346" changecount="1">
<pre>&lt;&lt;toolbar permalink&gt;&gt;
! Stable Release 1.0.10.48 - 1 February 2012
* upgraded LuaNginxModule to 0.4.1.
Components bundled with this release:
* ~LuaJIT-2.0.0-beta9
* array-var-nginx-module-0.03rc1
* auth-request-nginx-module-0.2
* drizzle-nginx-module-0.1.2rc6
* echo-nginx-module-0.38rc1
* encrypted-session-nginx-module-0.02
* form-input-nginx-module-0.07rc5
* headers-more-nginx-module-0.17rc1
* iconv-nginx-module-0.10rc5
* lua-5.1.4
* lua-cjson-1.0.3
* lua-rds-parser-0.04
* lua-redis-parser-0.09rc5
* memc-nginx-module-0.13rc3
* nginx-1.0.10
* ngx_devel_kit-0.2.17
* ngx_lua-0.4.1
* ngx_postgres-0.9rc2
* rds-csv-nginx-module-0.04
* rds-json-nginx-module-0.12rc7
* redis2-nginx-module-0.08rc2
* set-misc-nginx-module-0.22rc5
* srcache-nginx-module-0.13rc3
* upstream-keepalive-nginx-module-0.7
* xss-nginx-module-0.03rc8
! Mainline Version 1.0.10.47 - 29 January 2012
* upgraded LuaNginxModule to 0.4.1rc4.
** bugfix: {{{ngx_http_lua_header_filter_init}}} was called with an argument which actually accepts none. this could cause compilation errors at least with gcc 4.3.4 as reported in github [[issue #80|http://github.com/chaoslawful/lua-nginx-module/issues/80]]. thanks bigplum (Simon).
! Mainline Version 1.0.10.45 - 19 January 2012
* upgraded LuaNginxModule to 0.4.1rc3.
** bugfix: fixed all the warnings from the {{{clang}}} static analyzer.
** bugfix: [[ngx.exit|http://wiki.nginx.org/HttpLuaModule#ngx.exit]], [[ngx.redirect|http://wiki.nginx.org/HttpLuaModule#ngx.redirect]], [[ngx.exec|http://wiki.nginx.org/HttpLuaModule#ngx.exec]], and [[ngx.req.set_uri(uri, true)|http://wiki.nginx.org/HttpLuaModule#ngx.req.set_uri]] could return (they should never return as per the documentation). this bug had appeared in ngx_lua v0.3.1rc4 and ngx_openresty 1.0.6.13. thanks [[@cyberty|http://weibo.com/cyberty]] for reporting it.
** feature: allow use of the {{{DDEBUG}}} macro from the outside (via the {{{-D DDEBUG=1}}} cc opton).
* upgraded DrizzleNginxModule to v0.1.2rc6.
** bugfix: fixed all the warnings from the {{{clang}}} static analyzer.
** feature: allow use of the {{{DDEBUG}}} macro from the outside (via the {{{-D DDEBUG=1}}} cc opton).
* upgraded EchoNginxModule to 0.38rc1, SetMiscNginxModule to 0.22rc5, HeadersMoreNginxModule to 0.17rc1, and MemcNginxModule to 0.13rc3, to allow use of the {{{DDEBUG}}} macro from the outside (via the {{{-D DDEBUG=1}}} cc opton).
! Stable Release 1.0.10.44 - 16 January 2012
* upgraded EchoNginxModule to 0.37.
* upgraded HeadersMoreNginxModule to 0.16.
* bugfix: fixed compatibility of the packaging scripts on Darwin and *BSD. thanks nightsailer and Piotr Sikora.
Components bundled with this release:
* ~LuaJIT-2.0.0-beta9
* array-var-nginx-module-0.03rc1
* auth-request-nginx-module-0.2
* drizzle-nginx-module-0.1.2rc4
* echo-nginx-module-0.37
* encrypted-session-nginx-module-0.02
* form-input-nginx-module-0.07rc5
* headers-more-nginx-module-0.16
* iconv-nginx-module-0.10rc5
* lua-5.1.4
* lua-cjson-1.0.3
* lua-rds-parser-0.04
* lua-redis-parser-0.09rc5
* memc-nginx-module-0.13rc2
* nginx-1.0.10
* ngx_devel_kit-0.2.17
* ngx_lua-0.4.0
* ngx_postgres-0.9rc2
* rds-csv-nginx-module-0.04
* rds-json-nginx-module-0.12rc7
* redis2-nginx-module-0.08rc2
* set-misc-nginx-module-0.22rc4
* srcache-nginx-module-0.13rc3
* upstream-keepalive-nginx-module-0.7
* xss-nginx-module-0.03rc8
! Mainline Version 1.0.10.43 - 12 January 2012
* upgraded LuaNginxModule to 0.4.0.
* upgraded EncryptedSessionNginxModule to 0.02.
** bugfix: the {{{-lssl}}} option broke nginx linking when {{{--with-openssl=DIR}}} is specified. thanks charlieyang for reporting this issue.
* bugfix: fixed issues with relative path DIR in the {{{--with-openssl=DIR}}} option for {{{./configure}}}.
! Mainline Version 1.0.10.41 - 9 January 2012
* upgraded UpstreamKeepaliveNginxModule to 0.7.
** Bugfix: unbuffered connection might not be kept alive under Linux.
** Bugfix: module could not be built on Windows.
** Bugfix: module could not be built without the ngx_http_ssl_module.
** Feature: https connections support (requires patches).
** Bugfix: invalid connections might be cached.
** Bugfix: the &quot;[alert] ... open socket ... left in connection ...&quot; messages were logged on nginx worker process gracefull exit for each cached connection; the bug had appeared in version 0.3.
* upgraded EchoNginxModule to 0.37rc8.
** bugfix: fixed two spots that we did not check null pointers returned by the memory allocator.
** bugfix: attempt to fix places where {{{ngx_time_update}}} might not be compiled properly.
! Mainline Version 1.0.10.39 - 4 January 2012
* upgraded LuaNginxModule to v0.3.1rc45.
** bugfix: [[ngx.req.get_uri_args|http://wiki.nginx.org/HttpLuaModule#ngx.req.get_uri_args]] and [[ngx.req.get_post_args|http://wiki.nginx.org/HttpLuaModule#ngx.req.get_post_args]] now only parse up to 100 arguments by default. but one can specify the optional argument to these two methods to specify a custom maximum number of args. thanks Tzury Bar Yochay for reporting this.
** bugfix:  [[ngx.req.get_headers|http://wiki.nginx.org/HttpLuaModule#ngx.req.get_headers]] now only parse up to 100 request headers by default. but one can specify the optional argument to this method to specify a custom maximum number of headers.
! Mainline Version 1.0.10.37 - 30 December 2011
* upgraded LuaNginxModule to v0.3.1rc43.
** bugfix: removing builtin headers via [[ngx.req.clear_header|http://wiki.nginx.org/HttpLuaModule#ngx.req.clear_header]] and its equivalent in huge request headers with 20+ entries could result in data loss. thanks Chris Dumoulin for the patch in [[github issue #6|https://github.com/agentzh/headers-more-nginx-module/issues/6]].
** bugfix: could not compile with Nginx 1.1.12+ because Nginx 1.1.12 changed its regex API. now we call PCRE API directly and require at least PCRE 8.21 for the PCRE JIT support in our {{{ngx.re}}} API (since PCRE 8.20 had a bug in its JIT engine that it did not honor {{{pcre_malloc}}} and {{{pcre_free}}} at all).
! Mainline Version 1.0.10.35 - 30 December 2011
* upgraded HeadersMoreNginxModule to v0.16rc7.
** bugfix: removing builtin headers in huge request headers with 20+ entries could result in data loss. thanks Chris Dumoulin for the patch in [[github issue #6|https://github.com/agentzh/headers-more-nginx-module/issues/6]].
* bugfix: the {{{install}}} phony target did not depend on the {{{all}}} phony target in the Makefile generated by {{{./configure}}}. thanks [[姚伟斌|http://weibo.com/yaoweibin]] for reporting this issue.
! Mainline Version 1.0.10.33 - 29 December 2011
* bugfix: the {{{./configure}}} script's  {{{--add-module}}} option did not accept relative path values. thanks [[姚伟斌|http://weibo.com/yaoweibin]] for the patch.
! Mainline Version 1.0.10.31 - 25 December 2011
* upgraded LuaJIT to 2.0.0beta9.
** changes: http://luajit.org/changes.html
* upgraded LuaNginxModule to v0.3.1rc42.
** bugfix: [[ngx.req.set_header|http://wiki.nginx.org/HttpLuaModule#ngx.req.set_header]] might cause invalid memory reads because Nginx request header values must be null terminated. thanks Maxim Dounin.
** bugfix: {{{ngx.var.VARIABLE}}} might evaluate to nil even if there is a valid value because the Nginx variable value's {{{valid}}} flag might not be initialized properly. this bad had appeared in v0.3.1rc40.
* upgraded HeadersMoreNginxModule to v0.16rc6.
** bugfix: the [[more_set_input_headers|http://wiki.nginx.org/HttpHeadersMoreModule#more_set_headers]] directive might cause invalid memory reads because Nginx request header values must be null terminated. thanks Maxim Dounin.
! Mainline Version 1.0.10.29 - 17 December 2011
* upgraded LuaNginxModule to v0.3.1rc41.
** bugfix: [[ngx.req.set_header|http://wiki.nginx.org/HttpLuaModule#ngx.req.set_header]] and [[ngx.req.clear_header|http://wiki.nginx.org/HttpLuaModule#ngx.req.clear_header]] did not handle the {{{Accept-Encoding}}} request headers properly. thanks 天街夜色.
* upgraded HeadersMoreNginxModule to 0.16rc5.
** bugfix: [[more_set_input_headers|http://wiki.nginx.org/HttpHeadersMoreModule#more_set_input_headers]] and [[more_clear_input_headers|http://wiki.nginx.org/HttpHeadersMoreModule#more_clear_input_headers]] did not handle the {{{Accept-Encoding}}} request headers properly. thanks 天街夜色.
! Mainline Version 1.0.10.27 - 16 December 2011
* upgraded LuaNginxModule to v0.3.1rc40.
** bugfix: {{{ngx.flush(true)}}} could not be used before I/O calls like [[ngx.location.capture|http://wiki.nginx.org/HttpLuaModule#ngx.location.capture]]. this bug had appeared in v0.3.1rc34.
** bugfix: {{{ngx.var.VARIABLE}}} did not evaluate to {{{nil}}} when the Nginx variable's {{{valid}}} flag is {{{0}}}.
** docs: various documentation improvements. thanks Nginx User.
** bugfix: there were various places where we did not check the pointer returned by the memory allocator.
* upgraded SetMiscNginxModule to v0.22rc4.
** bugfix: fixed one place that does not check the pointer returned by the memory allocator.
** src: converted {{{CRLF}}} in the source files and test files to {{{LF}}}.
* bugfix: some old version of shell {{{cp}}} command does not support trailing slashes in the destination argument and could break our {{{./configure}}} script. thanks [[姚伟斌|http://weibo.com/yaoweibin]] for reporting it.
! Mainline Version 1.0.10.25 - 14 December 2011
* upgraded SrcacheNginxModule to v0.13rc3.
** bugfix: fixed a regression with XssNginxModule for cache hits. this bug had appeared in v0.13rc1. thanks [[万珣新|http://weibo.com/liseen]].
** bugfix: we did not cache the {{{Location}}} response header at all for {{{301}}}/{{{302}}} responses.
** bugfix: we should not blindly cache the {{{Accept-Ranges: bytes}}} response headers regardless of the actual current requests.
* upgraded XssNginxModule to v0.03rc8.
** bugfix: fixed a few debug-level log messages; the old text was misleading.
! Stable Release 1.0.10.24 - 11 December 2011
Same as the devel version 1.0.10.23.

Components bundled with this release:
* ~LuaJIT-2.0.0-beta8
* array-var-nginx-module-0.03rc1
* auth-request-nginx-module-0.2
* drizzle-nginx-module-0.1.2rc4
* echo-nginx-module-0.37rc7
* encrypted-session-nginx-module-0.01
* form-input-nginx-module-0.07rc5
* headers-more-nginx-module-0.16rc4
* iconv-nginx-module-0.10rc5
* lua-5.1.4
* lua-cjson-1.0.3
* lua-rds-parser-0.04
* lua-redis-parser-0.09rc5
* memc-nginx-module-0.13rc2
* nginx-1.0.10
* ngx_devel_kit-0.2.17
* ngx_lua-0.3.1rc38
* ngx_postgres-0.9rc2
* rds-csv-nginx-module-0.04
* rds-json-nginx-module-0.12rc7
* redis2-nginx-module-0.08rc2
* set-misc-nginx-module-0.22rc3
* srcache-nginx-module-0.13rc2
* upstream-keepalive-nginx-module-0.3
* xss-nginx-module-0.03rc7
! Mainline Version 1.0.10.23 - 5 December 2011
* upgraded RdsJsonNginxModule to v0.12rc7.
** added more debug level error log outputs to ease debugging (as discussed in [[github issue #2|https://github.com/agentzh/rds-json-nginx-module/issues/2]]).
* upgraded XssNginxModule to v0.03rc7.
** now we use the {{{ngx_log_debugN}}} macros to emit debugging outputs instead of {{{info}}} level error logging because the latter is costly in production.
* upgraded LuaNginxModule to 0.3.1rc38.
** added constant {{{ngx.HTTP_GATEWAY_TIMEOUT}}} (504) per Fry-kun in [[github issue #73|https://github.com/chaoslawful/lua-nginx-module/issues/73]].
! Mainline Version 1.0.10.21 - 30 November 2011
* fixed a serious regression for Linux AIO in [[nginx-1.0.10-epoll_check_stale_wev.patch|https://github.com/agentzh/ngx_openresty/blob/master/patches/nginx-1.0.10-epoll_check_stale_wev.patch]], thanks Maxim Dounin for the patch's patch.
! Mainline Version 1.0.10.19 - 29 November 2011
* upgraded LuaNginxModule to 0.3.1rc37.
** bugfix: use of the ngx.re API might lead to errors like {{{pcre_compile() failed: failed to get memory in ...}}} due to incorrect {{{pcre_malloc}}} and {{{pcre_free}}} handling. thanks Vittly for reporting this as [[github issue #72|https://github.com/chaoslawful/lua-nginx-module/issues/72]].
** docs: massive documentation improvements. thanks Nginx User.
! Mainline Version 1.0.10.17 - 26 November 2011
* upgraded LuaNginxModule to 0.3.1rc36.
** bugfix: fixed the {{{ngx_log_debugN}}} macros which failed to compile without {{{--with-debug}}}. thanks [[@ldmiao|http://weibo.com/ldmiao]] for reporting it.
! Mainline Version 1.0.10.15 - 26 November 2011
* upgraded LuaNginxModule to 0.3.1rc35.
** bugfix: now we check timed out downstream connections in our write event handler.
! Mainline Version 1.0.10.13 - 25 November 2011
* upgraded LuaNginxModule to 0.3.1rc34.
** feature: added {{{wait}}} boolean argument to [[ngx.flush()|http://wiki.nginx.org/HttpLuaModule#ngx.flush]] to support synchronous flushing; {{{ngx.flush(true)}}} will not return until all the data has been flushed into the system send buffer or the send timeout has expired.
! Mainline Version 1.0.10.11 - 24 November 2011
* upgraded LuaNginxModule to 0.3.1rc33.
** feature: added new Lua API [[ngx.now|http://wiki.nginx.org/HttpLuaModule#ngx.now]] to return the current time (including the ms part as the decimal part). thanks 林青.
** feature: added new Lua API [[ngx.update_time|http://wiki.nginx.org/HttpLuaModule#ngx.update_time]] to forcibly updating nginx's time cache.
** docs: massive documentation improvement done by Nginx User.
! Mainline Version 1.0.10.9 - 24 November 2011
* upgraded XssNginxModule to 0.03rc6.
** bugfix: now we use {{{signed char}}} explicitly instead of the vague {{{char}}} type which could be unsigned by default in certain systems like ~PowerPC. thanks Dmitry E. Oboukhov.
* upgraded MemcNginxModule to 0.13rc2.
** bugfix: now we use {{{signed char}}} explicitly instead of the vague {{{char}}} type which could be unsigned by default in certain systems like ~PowerPC. thanks Dmitry E. Oboukhov.
* upgraded Redis2NginxModule to 0.08rc2.
** bugfix: when {{{char}}} defaults to {{{unsigned char}}}, the Ragel-based Redis response parser could not accept non-ascci bytes. thanks Dmitry E. Oboukhov.
! Mainline Version 1.0.10.7 - 23 November 2011
* upgraded LuaNginxModule to 0.3.1rc31.
** feature: added opions {{{copy_all_vars}}} and {{{vars}}} to [[ngx.location.capture|http://wiki.nginx.org/HttpLuaModule#ngx.location.capture]] and [[ngx.location.capture_multi|http://wiki.nginx.org/HttpLuaModule#ngx.location.capture_multi]]. thanks Marcus Clyne for the patch.
** bugfix: fixed a bad regression in [[ngx.location.capture_multi|http://wiki.nginx.org/HttpLuaModule#ngx.location.capture_multi]] when the request option table is specified. this bug had appeared in ngx_lua 0.3.1rc26 and ngx_openresty 1.0.9.1.
! Mainline Version 1.0.10.5 - 21 November 2011
* upgraded LuaNginxModule to 0.3.1rc30.
** feature: added new regex options {{{&quot;j&quot;}}} and {{{&quot;d&quot;}}} to [[ngx.re.match|http://wiki.nginx.org/HttpLuaModule#ngx.re.match]], [[ngx.re.gmatch|http://wiki.nginx.org/HttpLuaModule#ngx.re.gmatch]], [[ngx.re.sub|http://wiki.nginx.org/HttpLuaModule#ngx.re.sub]], and [[ngx.re.gsub|http://wiki.nginx.org/HttpLuaModule#ngx.re.gsub]]  so as to enable the PCRE [[JIT mode|http://www.manpagez.com/man/3/pcrejit/]] and DFA mode, respectively. thanks [[@姜大炮|http://weibo.com/egis]] for providing the patch.
! Mainline Version 1.0.10.3 - 17 November 2011
* upgraded LuaNginxModule to 0.3.1rc29.
** feature: added [[ngx.hmac_sha1|http://wiki.nginx.org/HttpLuaModule#ngx.hmac_sha1]]. thanks [[drdrxp|http://weibo.com/drdrxp]].
** docs: documented the long-existent [[ngx.md5|http://wiki.nginx.org/HttpLuaModule#ngx.md5]] and [[ngx.md5_bin|http://wiki.nginx.org/HttpLuaModule#ngx.md5_bin]] ~APIs.
** docs: massive documentation improvements. thanks Nginx User.
! Mainline Version 1.0.10.1 - 16 November 2011
* upgraded the Nginx core to 1.0.10.

See [[ChangeLog1000009]] for change log for ngx_openresty 1.0.9.x.
</pre>
</div>
<div title="ChangeLog1000011" creator="YichunZhang" modifier="YichunZhang" created="201205131345" changecount="1">
<pre>&lt;&lt;toolbar permalink&gt;&gt;
! Stable Release 1.0.11.28 - 25 March 2012
Same as the devel release 1.0.11.27.

Components bundled with this release:
* ~LuaJIT-2.0.0-beta9 (hotfix #1)
* array-var-nginx-module-0.03rc1
* auth-request-nginx-module-0.2
* drizzle-nginx-module-0.1.2rc6
* echo-nginx-module-0.38rc2
* encrypted-session-nginx-module-0.02
* form-input-nginx-module-0.07rc5
* headers-more-nginx-module-0.17rc1
* iconv-nginx-module-0.10rc7
* lua-5.1.4
* lua-cjson-1.0.3
* lua-rds-parser-0.04
* lua-redis-parser-0.09rc7
* lua-resty-memcached-0.06
* lua-resty-mysql-0.06
* lua-resty-redis-0.08
* lua-resty-string-0.05
* lua-resty-upload-0.02
* memc-nginx-module-0.13rc3
* nginx-1.0.11
* ngx_coolkit-0.2rc1
* ngx_devel_kit-0.2.17
* ngx_lua-0.5.0rc21
* ngx_postgres-0.9
* rds-csv-nginx-module-0.05rc1
* rds-json-nginx-module-0.12rc7
* redis2-nginx-module-0.08rc4
* set-misc-nginx-module-0.22rc5
* srcache-nginx-module-0.13rc6
* upstream-keepalive-nginx-module-0.7
* xss-nginx-module-0.03rc9
! Mainline Version 1.0.11.27 - 22 March 2012
* upgraded LuaNginxModule to 0.5.0rc21.
** bugfix: made the sha1 library (or ~OpenSSL) an optional dependency, as suggested by runner-mei in [[github issue #94|https://github.com/chaoslawful/lua-nginx-module/issues/94]].
** bugfix: we did not declare C variables at the beginning of the current code block in {{{ngx_http_lua_del_thread}}}, reported by runner-mei in [[github issue #93|https://github.com/chaoslawful/lua-nginx-module/issues/93]].
** bugfix: incorrectly used {{{ngx_conf_log_error}}} by feeding {{{NGX_ERROR}}} as the first argument, as reported by runner-mei in [[github issue #92|https://github.com/chaoslawful/lua-nginx-module/issues/92]].
** bugfix: spelling errors in Lua exception message text.
* upgraded EchoNginxModule to 0.38rc2.
** bugfix: [[$echo_request_body|http://wiki.nginx.org/HttpEchoModule#.24echo_request_body]] variable was not able to work on arbitrary request body chains (i.e., more than 2 chain links), just like the standard [[$request_body|http://wiki.nginx.org/HttpCoreModule#.24request_body]] variable that only processes the first two chain links. now [[$echo_request_body|http://wiki.nginx.org/HttpEchoModule#.24echo_request_body]] no longer has this limitation.
* applied the upstream_pipelining patch to the nginx core, as discussed here: http://mailman.nginx.org/pipermail/nginx-devel/2012-March/002040.html this patch is required at least for the pipelined requests support in nginx upstream modules.
! Mainline Version 1.0.11.25 - 16 March 2012
* applied the null-character-fixes patch from the mainstream. The bug did result in a disclosure of previously freed memory if upstream server returned specially crafted response, potentially exposing sensitive information.
* upgraded SrcacheNginxModule to 0.13rc6.
** bugfix: fixed a typo in an error message text for response truncation check in [[srcache_store|http://wiki.nginx.org/HttpSRCacheModule#srcache_store]].
! Mainline Version 1.0.11.23 - 15 March 2012
* upgraded LuaNginxModule to 0.5.0rc19.
** feature: added new directive [[lua_http10_buffering|http://wiki.nginx.org/HttpLuaModule#lua_http10_buffering]] which is {{{on}}} by default.
** feature: added new constant {{{ngx.DECLINED}}}.
** bugfix: [[access_by_lua|http://wiki.nginx.org/HttpLuaModule#access_by_lua]] could not work with the {{{satisfy any}}} configuration.
** bugfix: now we recycle the special flush buffer and chain link for [[ngx.flush|http://wiki.nginx.org/HttpLuaModule#ngx.flush]] to prevent request-scoped memory leaks when emitting long data streams to the downstream.
* upgraded SrcacheNginxModule to 0.13rc5.
** feature: now [[srcache_store|http://wiki.nginx.org/HttpSRCacheModule#srcache_store]] discards responses that are obviously truncated when the actual output data is shorter than what is declared in its {{{Content-Length}}} response header. thanks Greg Grensteiner.
** bugfix: the access phase handler actually ran in a phase later than the {{{access}}} phase.
** bugfix: HTTP HEAD requests that lead to a cache hits would cause memory issues like invalid reads.
* upgraded LuaRestyRedisLibrary to 0.08.
** feature: implemened redis pipelining API by adding new methods {{{init_pipeline}}}, {{{commit_pipeline}}}, and {{{cancel_pipeline}}}.
! Mainline Version 1.0.11.21 - 7 March 2012
* upgraded LuaRestyRedisLibrary to 0.07.
** feature: added the [[evalsha|http://redis.io/commands/eval]] command to the redis command table. thanks Chris Love.
* upgraded LuaRestyStringLibrary to 0.05.
** feature: added new modules {{{resty.sha224}}}, {{{resty.sha256}}}, {{{resty.sha384}}}, and {{{resty.sha512}}} to exposes the ~OpenSSL API for the complete {{{SHA-2}}} hash function set. thanks @lhmwzy.
* upgraded LuaNginxModule to 0.5.0rc17.
** bugfix: time stamps could overflow on 32-bit systems in the shared dict API; now we explicitly use 64-bit integers.
** feature: added new method {{{flush_all}}} to the shared dict. thanks Weiqiang Li.
** docs: documented the max concurrent subrequest count limitation and max error log line size limit.
! Mainline Version 1.0.11.19 - 1 March 2012
* upgraded Redis2NginxModule to 0.08rc4.
** bugfix: redis &quot;nil multi bulk replies&quot; did not parse at all. thanks 郭颖 for reporting this issue.
* upgraded LuaRedisParserLibrary to 0.09rc7.
** bugfix: redis &quot;nil multi bulk replies&quot; did not parse at all. thanks 郭颖 for reporting this issue.
! Mainline Version 1.0.11.17 - 29 February 2012
* feature: bundle LuaRestyMySQLLibrary 0.06, which is enabled by default.
* feature: bundle LuaRestyRedisLibrary 0.06, which is enabled by default.
* feature: bundle LuaRestyMemcachedLibrary 0.06, which is enabled by default.
* feature: bundle LuaRestyUploadLibrary 0.02, which is enabled by default.
* feature: bundle LuaRestyStringLibrary 0.04, which is enabled by default.
* bugfix: no longer enable {{{-DLUAJIT_USE_VALGRIND}}} for LuaJIT when {{{--with-debug}}} option is specified.
* bugfix: applied the official [[hotfix #1 patch|http://luajit.org/download/beta9_hotfix1.patch]] for LuaJIT 2.0.0 beta9.
* feature: raised the default {{{NGX_HTTP_MAX_SUBREQUESTS}}} to 200, in sync with the official repository.
* upgraded LuaNginxModule to 0.5.0rc16.
** bugfix: the shared dict storage might leak memory in the store: {{{ngx_http_lua_shdict_lookup}}} incorrectly assumed that nodes with identical keys are linked together, which might not be true after tree re-balancing. thanks the patch from Lanshun Zhou.
** optimize: removed a redundant piece of code for subrequest {{{headers_in}}} fixes in {{{ngx_http_lua_adjust_subrequest}}}.
! Mainline Version 1.0.11.15 - 24 February 2012
* now we enable the {{{-DLUAJIT_USE_VALGRIND -DLUA_USE_APICHECK -DLUA_USE_ASSERT}}} flags for LuaJIT when the {{{--with-debug}}} option is specified.
* apply the [[max_subrequests patch|https://github.com/agentzh/ngx_openresty/blob/master/patches/nginx-1.0.11-max_subrequests.patch]] to allow the {{{NGX_HTTP_MAX_SUBREQUESTS}}} macro to be overridden from the outside and adjusted the default value from 50 to 100 because 50 is a little too conservative.
* upgraded XssNginxModule to 0.03rc9, RdsCsvNginxModule to 0.05rc1, and Redis2NginxModule to 0.08rc3, allowing enabling {{{DDEBUG=1}}} globally.
* upgraded IconvNginxModule to 0.10rc7.
** bugfix: enabling {{{DDEBUG=1}}} globally lead to compilation errors.
** bugfix: could not work with HTTP 1.0 requests.
** optimize: only register output filters when the {{{iconv_filter}}} is actually used in the config file.
* upgraded LuaNginxModule to 0.5.0rc15.
** bugfix: the {{{exptime}}} argument to {{{shdict:set/add/etc}}} methods was incorrectly ignored when the {{{flags}}} argument is also specified. thanks the patch from Brian Akins.
** docs: fixed various typos in docs. thanks 王斌.
** bugfix: for big input data, the cosocket reading methods could result in crashes due to incorrect use of {{{luaL_Buffer}}}. now we eliminate {{{luaL_Buffer}}} altogether by managing the recv buffers ourselves. the recv buffers can also be recycled.
** bugfix: now we avoid using {{{luaL_checkstring}}} which could do another {{{long jump}}} on its own. thanks 王斌.
** bugfix: {{{tcpsocket:setkeepalive()}}} did not return errors when the current connection has readable data or there is still unread data in the LuaNginxModule upstream buffer.
** bugfix: cosocket methods no longer explicitly return {{{nil}}} error strings upon success.
** bugfix: when the parent request takes a request body, the subrequest does not take any bodies, and the subrequest's method is neither {{{PUT}}} nor {{{POST}}}, then the subrequest will no longer inherit the parent request's request body. thanks 欧远宁 for reporting this issue.
** bugfix: data might be accidentally read into the Lua space on idle sockets when the last operation is a read operation *and* a read event suddenly arrives for edge-triggered event models. the same might also apply to write operations too.
** bugfix: invalid reads might happen in the reading iterators returned by the {{{receiveuntil()}}} method which could lead to segfaults. this was a bug in the DFA minimizer's optimized code path.
** bugfix: the {{{closed}}} error would occur for long-running requests that hold the idle cosocket object for a period of time that is longer than the read timeout setting: we should delete the read event timer in time when the {{{receive}}} call has already got a read event. thanks 欧远宁 for reporting this issue.
** logs: added error logs for cosocket timeout errors.
** logs: added detailed error logs for cosocket {{{closed}}} errors.
** optimize: introduced a minor optimization that we can save one {{{recv}}} call when the read event is active *and* the read event is not ready.
** optimize: now we recycle the downstream output buffers to save memory and dynamic allocation times for long-running requests with huge outputs.
** bugfix: C macro directives were used inside a C macro argument which made (at least) gcc 3.2.3 unhappy. thanks Feng Bin.
! Mainline Version 1.0.11.11 - 14 February 2012
* upgraded LuaNginxModule to 0.5.0rc7.
** bugfix: cosocket API could not be used before [[ngx.location.capture|http://wiki.nginx.org/HttpLuaModule#ngx.location.capture]] and its friends for fast network access: [[tcpsock:send()|http://wiki.nginx.org/HttpLuaModule#tcpsock:send]] method did not reset {{{u-&gt;waiting}}} properly. thanks 欧远宁.
! Mainline Version 1.0.11.9 - 13 February 2012
* upgraded LuaNginxModule to 0.5.0rc6.
** bugfix: could not compile with nginx 0.8.x. thanks 欧远宁. this bug had appeared in LuaNginxModule v0.5.0rc1.
** feature: added the [[ngx.sha1_bin|http://wiki.nginx.org/HttpLuaModule#ngx.sha1_bin]] method which returns the binary form of the {{{SHA-1}}} digest.
** bugfix: we incorrectly allowed {{{ngx.null}}} in the string table argument to [[cosocket:send()|http://wiki.nginx.org/HttpLuaModule#tcpsock:send]] method.
** feature: allow use of ngx.null in [[ngx.log()|http://wiki.nginx.org/HttpLuaModule#ngx.log]] and [[print()|http://wiki.nginx.org/HttpLuaModule#print]] arguments.
* added Piotr Sikora's CoolkitNginxModule 0.2rc1 to the bundle, which is also enabled by default.
! Mainline Version 1.0.11.7 - 7 February 2012
* upgraded LuaNginxModule to 0.5.0rc5.
** feature: added constant {{{ngx.null}}} which is a {{{NULL}}} light userdata to represent {{{nil}}} values in Lua tables and etc. this is compatible with at least [[lua-cjson|http://www.kyne.com.au/~mark/software/lua-cjson.php]] library's {{{cjson.null}}} constant.
! Mainline Version 1.0.11.5 - 7 February 2012
* upgraded LuaNginxModule to 0.5.0rc4.
** bugfix: setting {{{ngx.header.last_modified}}} was not implemented fully. thanks Brian Akins.
! Mainline Version 1.0.11.3 - 6 February 2012
* upgraded LuaNginxModule to 0.5.0rc3.
** feature: now [[tcpsocket:send()|http://wiki.nginx.org/HttpLuaModule#tcpsock:send]] method supports lua tables of string fragments which can save unnecessary string concatenation operations on the Lua land that are usually quite expensive.
** bugfix: fixed issues in debugging logs for the cosocket API.
** feature: added user flags support to the [[shared dictionary API|http://wiki.nginx.org/HttpLuaModule#ngx.shared.DICT]]. thanks Brian Akins.
* upgraded LuaRedisParserLibrary to 0.09rc6.
** bugfix: remove unneeded string push operations. thanks Brian Akins.
! Mainline Version 1.0.11.1 - 2 February 2012
* upgraded the Nginx core to 1.0.11.
** see the changes here: http://nginx.org/en/CHANGES-1.0
* upgraded LuaNginxModule to 0.5.0rc1.
** feature: implemented the coroutine-based TCP and Unix Domain client socket API (aka the &quot;cosocket&quot; API) that is mostly compatible with the [[LuaSocket|http://w3.impa.br/~diego/software/luasocket/tcp.html]] library.
** feature: implemented built-in connection pool support for the cosocket API.
** feature: added new function [[ngx.req.socket()|http://wiki.nginx.org/HttpLuaModule#ngx.req.socket]] to return a cosocket object for the downstream connection so as to do streaming request body reading. thanks Taylor Weibley for sponsoring the development work.
** optimization: optimized the chain-link and buf recycle logic for the subrequest API and make it share free buffers with the cosocket API.
* upgraded PostgresNginxModule to 0.9.
** bugfix: Fix compatibility with poll, select and /dev/poll event models.
** bugfix: Fix compatibility with ~PostgreSQL 9.x.
** bugfix: Fix compatibility with nginx-1.1.4+.

See ChangeLog1000010 for change log for ngx_openresty 1.0.10.x.</pre>
</div>
<div title="ChangeLog1000015" creator="YichunZhang" modifier="YichunZhang" created="201205131345" modified="201205161050" changecount="2">
<pre>&lt;&lt;toolbar permalink&gt;&gt;
! Mainline Version 1.0.15.5 - 16 May 2012
* upgraded LuaJIT to 2.0.0beta10.
** see changes here: http://luajit.org/changes.html
* feature: added the {{{--with-luajit-xcflags=FLAGS}}} option to {{{./configure}}} to add more C compiler options to LuaJIT's build system.
* upgraded LuaNginxModule to 0.5.0rc28.
** bugfix: [[ngx.req.socket()|http://wiki.nginx.org/HttpLuaModule#ngx.req.socket]] did not honor the {{{Expect: 100-continue}}} request header and could hang. thanks Matthieu Tourne for the patch in [[pull request #107|https://github.com/chaoslawful/lua-nginx-module/pull/107]].
** bugfix: the [[ngx.req.socket()|http://wiki.nginx.org/HttpLuaModule#ngx.req.socket]] object (i.e., the downstream cosocket object) did not work with HTTP 1.1 pipelined requests at all.
** bugfix: the [[ngx.req.socket()|http://wiki.nginx.org/HttpLuaModule#ngx.req.socket]] object might lose the last part of the request body when receiving data. this regression had appeared in v0.5.0rc25. thanks Matthieu Tourne for reporting it.
** feature: detailed backtraces (Lua callstack) will be automatically printed to {{{error.log}}} when the user Lua code is interrupted by Lua exceptions. thanks Matthieu Tourne for the patch in [[pull request #107|https://github.com/chaoslawful/lua-nginx-module/pull/107]].
** optimize: removed dead code found by Simon Liu via scan-build.
* upgraded RdsCsvNginxModule to 0.05rc2.
** bugfix: the output buffer size would get wrong when the {{{affected_rows}}} field is larger than a single-digit number. thanks Wendal Chen for reporting this by using clang.
* upgraded LuaRestyStringLibrary to 0.06.
** added new Lua module {{{resty.random}}} that implements secure random and pseudo-random string generators. thanks Chase Colman for the patch.
** added new Lua module {{{resty.aes}}} that exposes the AES submodule of ~OpenSSL via LuaJIT FFI. thanks Chase Colman for the patch.
! Mainline Version 1.0.15.3 - 13 May 2012
* now we bundle Sergey A. Osokin's RedisNginxModule, 0.3.6, which is also enabled by default. thanks Zhu Feng for requesting this.
* upgraded LuaNginxModule to 0.5.0rc27.
** bugfix: nginx could crash on request finalization when running the cosocket cleanup handle due to the lack of check of the {{{ctx}}} pointer. thanks shaneeb for reporting this in [[github issue #110|https://github.com/chaoslawful/lua-nginx-module/issues/110]].
** bugfix: [[ngx.req.get_body_data()|http://wiki.nginx.org/HttpLuaModule#ngx.req.get_body_data]] could not handle multi-buffer request bodies and discarded the body data after the first buffer.
** bugfix: [[ngx.ctx|http://wiki.nginx.org/HttpLuaModule#ngx.ctx]] was not accessible at all in {{{set_by_lua*}}}. thanks Pierre.
** bugfix: fixed typo &quot;on-array&quot;, which should be &quot;non-array&quot;, in an error message.
** optimize: now [[ngx.log|http://wiki.nginx.org/HttpLuaModule#ngx.log]] is much faster when the log level argument is lower than the actual [[error_log|http://wiki.nginx.org/CoreModule#error_log]] level specified in nginx.conf. thanks Matthieu Tourne for providing the patch.
** optimize: now we call {{{ngx_http_lua_socket_finalize}}} in {{{cosocket:setkeepalive()}}} to help buffer reuse.
* upgraded SetMiscNginxModule to 0.22rc8.
** feature: added new directives [[set_secure_random_alphanum|http://wiki.nginx.org/HttpSetMiscModule#set_secure_random_alphanum]] and [[set_secure_random_lcalpha|http://wiki.nginx.org/HttpSetMiscModule#set_secure_random_lcalpha|]] for generating cryptographically strong random strings based on the {{{/dev/urandom}}} device. thanks Jeremy Wohl for the patch.
* upgraded SrcacheNginxModule to 0.13rc8.
** bugfix: the {{{Content-Length}}} response header for HEAD requests should leave intact when cache hits happen.
** bugfix: the [[srcache_store|http://wiki.nginx.org/HttpSRCacheModule#srcache_store]] subrequest did not set the {{{Content-Length}}} request header properly for multi-buffer request bodies. thanks Feibo Lee for submitting the patch.
** feature: HTTP conditional GET requests are now supported (both {{{If-Modified-Since}}} and {{{If-Unmodified-Since}}} request headers are properly handled). thanks ~Nginx_User777.
* upgraded LuaRedisParserLibrary to v0.09.
** feature: added {{{redis.parser._VERSION}}}.
** bugfix: now we use Lua userdata to allocate memory used on the C side to prevent potential leaks caused by malloc/free, as discussed in [[github issue #6|https://github.com/agentzh/lua-redis-parser/issues/6]].
* upgraded LuaRdsParserLibrary to 0.05.
** feature: added {{{redis.parser._VERSION}}}.
** bugfix: now we use Lua userdata to allocate memory used on the C side to prevent potential leaks caused by malloc/free, as discussed in [[github issue #6|https://github.com/agentzh/lua-redis-parser/issues/6]].
* upgraded LuaRestyMemcachedLibrary to 0.07.
** feature: now the methods for the Memcached storage commands now accept Lua tables as the {{{value}}} argument. thanks Brian Akins for the patch.
* upgraded LuaRestyUploadLibrary to 0.03.
** feature: now the raw headers for each part are also returned, as suggested by zou2062 in [[github issue #1|https://github.com/agentzh/lua-resty-upload/issues/1]].
* applied the patch for a bug in {{{ngx_http_named_location}}} to the nginx core: http://mailman.nginx.org/pipermail/nginx-devel/2012-May/002166.html
* applied the patch for a bug in the filter finalizer to the nginx core: http://mailman.nginx.org/pipermail/nginx-devel/2012-May/002190.html
! Mainline Version 1.0.15.1 - 29 April 2012
* upgraded the Nginx core to 1.0.15.
* bugfix: now we also add {{{&lt;openresty_prefix&gt;/lualib/?/init.lua}}} to the default {{{package.path}}} search list. thanks bigplum for reporting this issue.
* upgraded LuaNginxModule to 0.5.0rc25.
** bugfix: cosocket connections from the connection pool might lead to segfaults if it is not used immediately. thanks xukaifu for reporting this as [[github issue #108|https://github.com/chaoslawful/lua-nginx-module/issues/108]].
** bugfix: debug logging in the cosocket receive line method could lead to invalid memory reads under extreme network conditions. this issue was caught by [[mockeagain|https://github.com/agentzh/mockeagain]] in reading mode.
** bugfix: downstream cosockets might hang on the receive(size) method call for slow connections. [[mockeagain|https://github.com/agentzh/mockeagain]] in reading mode caught this issue.
** bugfix: we no longer forcibly quit the lua threads by clearing out its environment and running it blindly to the end because Lua GC will collect all those unfinished coroutines anyway.
** bugfix: improved the longjmp handling when Lua panic happens.
** bugfix: certain compilers might complain about missing declarations for types like {{{int8_t}}}. now we explicitly included {{{stdint.h}}}. thanks runner-mei for reporting it in [[github issue #98|https://github.com/chaoslawful/lua-nginx-module/issues/98]].
** feature: added new constant {{{ngx.HTTP_OPTIONS}}} for the HTTP OPTIONS method.
** feature: added support for OPTIONS method in the subrequest capture ~APIs. thanks Jónas Tryggvi Jóhannsson for requesting this in [[github issue #102|https://github.com/chaoslawful/lua-nginx-module/issues/102]].
** feature: added publich C functions {{{ngx_http_lua_add_package_preload}}}, {{{ngx_http_lua_get_global_state}}}, and {{{ngx_http_lua_get_request}}} to help other Nginx C modules expose new functionalities to LuaNginxModule. thanks Brian Akins for suggesting them in [[github pull request #101|https://github.com/chaoslawful/lua-nginx-module/pull/101]].
** feature: made {{{ngx_http_lua_api.h}}} visible to other Nginx modules by adding {{{src/api/}}} to the {{{CORE_INCS}}} config variable value in the config file. thanks Brian Akins for suggesting this in [[github pull request #105|https://github.com/chaoslawful/lua-nginx-module/pull/105]].
** feature: add the {{{gdbinit}}} script to ease Lua user code debugging (Wang Xiaozhe).
** optimize: various optimizations in cosocket's timeout handling. this gives about 2.5+% performance boost in some benchmarks using LuaRestyRedisLibrary and LuaRestyMySQLLibrary.
* upgraded RdsJsonNginxModule to v0.12rc8.
** bugfix: Microsoft C compilers complained about missing declarations of the type {{{int8_t}}}. now we explicitly include {{{stdint.h}}}. thanks runner-mei for reporting this issue in [[github issue #3|https://github.com/agentzh/rds-json-nginx-module/issues/3]].
* upgraded SetMiscNginxModule to 0.22rc7.
** bugfix: we should omit the [[set_sha1|http://wiki.nginx.org/HttpSetMiscModule#set_sha1]] directive when we do not have any ~SHA1 libraries (including ~OpenSSL) installed. thanks runner-mei for reporting this in [[github issue #9|https://github.com/agentzh/set-misc-nginx-module/issues/9]].
** feature: added new config directive [[set_rotate|http://wiki.nginx.org/HttpSetMiscModule#set_rotate]].
* upgraded DrizzleNginxModule to 0.1.2rc7.
** bugfix: reading data on a reused ~MySQL connection (coming from the connection pool) could hang due to the inactive read event when {{{poll}}} event API is used in nginx.
* upgraded LuaRestyMySQLLibrary to 0.07.
** fixed a typo in error messages.
** skipped parsing those column fields that we do not use (yet). this makes things noticeably faster.

See ChangeLog1000011 for change log for ngx_openresty 1.0.11.x.</pre>
</div>
<div title="ChangeLog1002001" creator="ZoomQuiet" modifier="ZoomQuiet" created="201206250649" changecount="1">
<pre>&lt;&lt;toolbar permalink&gt;&gt;
! Mainline Version 1.2.1.3 - 25 June 2012
* upgraded EchoNginxModule to 0.40.
** feature: added new directive [[echo_status|http://wiki.nginx.org/HttpEchoModule#echo_status]] which can be used to specify a different default response status code other than 200. thanks Maxime Corbeau for requesting this.
* upgraded LuaNginxModule to 0.5.3.
** bugfix: [[ngx.say|http://wiki.nginx.org/HttpLuaModule#ngx.say]] and [[ngx.print|http://wiki.nginx.org/HttpLuaModule#ngx.print]] might cause nginx to crash when table-typed arguments were given. thanks sztanpet for reporting this in [[github issue #54|https://github.com/chaoslawful/lua-nginx-module/issues/54#issuecomment-6527745]].
* applied [[location_if_inherits_proxy.patch|https://github.com/agentzh/ngx_openresty/blob/master/patches/nginx-1.2.1-location_if_inherits_proxy.patch]] to the nginx core. see http://mailman.nginx.org/pipermail/nginx-devel/2012-June/002374.html for details.
! Mainline Version 1.2.1.1 - 22 June 2012
* upgraded the Nginx core to 1.2.1.
** see the change log: http://nginx.org/en/CHANGES-1.2
* upgraded LuaNginxModule to 0.5.2.
** bugfix: [[header_filter_by_lua|http://wiki.nginx.org/HttpLuaModule#header_filter_by_lua]]* did not run at all when [[body_filter_by_lua|http://wiki.nginx.org/HttpLuaModule#body_filter_by_lua]]* was defined at the same time. thanks Tzury Bar Yochay for reporting this issue.
** feature: added the {{{inclusive}}} option to the [[cosocket:receiveuntil|http://wiki.nginx.org/HttpLuaModule#tcpsock:receiveuntil]] method to include the delimiter pattern string in the resulting data read. thanks Matthieu Tourne for the patch.
** optimize: merged two successive Nginx pool allocations in {{{ngx_http_lua_socket_resolve_handler}}} to reduce overhead.
* upgraded EchoNginxModule to 0.39.
** bugfix: EchoNginxModule's configure directives was not inherited automatically by {{{location if}}} inner blocks.
** bugfix: the old HTTP 1.0 protocol handling was wrong. we should leave that to the Nginx core and just output responses without a {{{Content-Length}}} response header.
** bugfix: reading the [[$echo_it|http://wiki.nginx.org/HttpEchoModule#.24echo_it]] variable outside the [[echo_foreach_split|http://wiki.nginx.org/HttpEchoModule#echo_foreach_split]] loop resulted in memory invalid reads and hence segfaults; now it is evaluates to the special {{{not found}}} value. thanks baqs for reporting this.
* upgraded PostgresNginxModule to 1.0rc1.
** bugfix: memory leak might happen if nginx 1.1.14+ was used and (at least) {{{libpq}}} failed to connect to the remote database.
* upgraded the (optional) no-pool patch to the latest version, {{{642ae25}}}.
** bugfix: we should postpone freeing the {{{elts}}} storage for {{{ngx_array_t}}} to {{{ngx_array_destroy}}} when resizing the array because at least the {{{ngx_rewrite}}} module stores external references to the array elements.

See ChangeLog1000015 for change log for ngx_openresty 1.0.15.x.</pre>
</div>
<div title="ChangeLog1002003" creator="YichunZhang" modifier="YichunZhang" created="201210142037" changecount="1">
<pre>&lt;&lt;toolbar permalink&gt;&gt;
! Stable Release 1.2.3.8 - 8 October 2012
This release is essentially the same as the development version 1.2.3.7.

The following components are bundled:
* ~LuaJIT-2.0.0-beta10
* array-var-nginx-module-0.03rc1
* auth-request-nginx-module-0.2
* drizzle-nginx-module-0.1.4
* echo-nginx-module-0.41
* encrypted-session-nginx-module-0.02
* form-input-nginx-module-0.07rc5
* headers-more-nginx-module-0.18
* iconv-nginx-module-0.10rc7
* lua-5.1.5
* lua-cjson-1.0.3
* lua-rds-parser-0.05
* lua-redis-parser-0.10
* lua-resty-dns-0.08
* lua-resty-memcached-0.08
* lua-resty-mysql-0.10
* lua-resty-redis-0.14
* lua-resty-string-0.06
* lua-resty-upload-0.03
* memc-nginx-module-0.13rc3
* nginx-1.2.3
* ngx_coolkit-0.2rc1
* ngx_devel_kit-0.2.17
* ngx_lua-0.6.10
* ngx_postgres-1.0rc2
* rds-csv-nginx-module-0.05rc2
* rds-json-nginx-module-0.12rc10
* redis-nginx-module-0.3.6
* redis2-nginx-module-0.09
* set-misc-nginx-module-0.22rc8
* srcache-nginx-module-0.16
* xss-nginx-module-0.03rc9
! Mainline Version 1.2.3.7 - 6 October 2012
* upgraded LuaNginxModule to 0.6.10.
** feature: now [[ngx.req.get_headers()|http://wiki.nginx.org/HttpLuaModule#ngx.req.get_headers]] returns a Lua table with keys in the all-lower-case form by default. thanks James Hurst and Matthieu Tourne for the feature request.
** feature: now [[ngx.req.get_headers()|http://wiki.nginx.org/HttpLuaModule#ngx.req.get_headers]] adds an {{{__index}}} metamethod to the resulting Lua table by default, which will automatically normalize the lookup key by converting upper-case letters and underscores in case of a lookup miss. thanks James Hurst and Matthieu Tourne for suggesting this feature.
** feature: now [[ngx.req.get_headers()|http://wiki.nginx.org/HttpLuaModule#ngx.req.get_headers]] accepts a second (optional) argument, {{{raw}}}, for controlling whether to return the original form of the header names (that is, the original letter-case).
** feature: added public C API functions {{{ngx_http_shared_dict_get}}} and {{{ngx_http_lua_find_zone}}} to allow other Nginx C modules or a patched Nginx core to directly access the [[shared memory dictionaries|http://wiki.nginx.org/HttpLuaModule#lua_shared_dict]] created by LuaNginxModule. thanks Piotr Sikora for requesting this feature.
** bugfix: fixed a compilation warning in the TCP/stream cosocket codebase when using (at least) gcc 3.4.6 for MIPS. thanks Dirk Feytons for reporting this as [[GitHub issue #162|https://github.com/chaoslawful/lua-nginx-module/issues/162]].
! Mainline Version 1.2.3.5 - 1 October 2012
* upgraded LuaNginxModule to 0.6.8.
** bugfix: [[ngx.re.gmatch|http://wiki.nginx.org/HttpLuaModule#ngx.re.gmatch]] might loop infinitely when the pattern matches an empty string. thanks Lance Li and xingxing for tracking this issue down.
** bugfix: pattern matching an empty substring in [[ngx.re.gmatch|http://wiki.nginx.org/HttpLuaModule#ngx.re.gmatch]] did not match at the end of the subject string.
** bugfix: [[ngx.re.gsub|http://wiki.nginx.org/HttpLuaModule#ngx.re.gsub]] might enter infinite loops because it could not handle patterns matching empty strings properly.
** bugfix: [[ngx.re.gsub|http://wiki.nginx.org/HttpLuaModule#ngx.re.gsub]] incorrectly skipped matching altogether when the subject string was empty.
! Mainline Version 1.2.3.3 - 26 September 2012
* upgraded LuaNginxModule to 0.6.7.
** feature: implemented the [[shdict:flush_expired(max_count?)|http://wiki.nginx.org/HttpLuaModule#ngx.shared.DICT.flush_expired]] method for flushing out and removing expired items up to {{{max_count}}} (or unlimited when {{{max_count == 0}}}). thanks Brian Akins for the patch.
** optimize: [[tcpsock:send()|http://wiki.nginx.org/HttpLuaModule#tcpsock:send]] now calls {{{c-&gt;send()}}} instead of {{{ngx_output_chain()}}}, which gives about 4% ~ 5% performance boost for a simple test case accessing Redis for several times.
** optimize: we now skip processing in the default write event handler when the write event is not ready.
** refactor: the I/O scheduler has been rewritten to keep track of the coroutine associated with each (non-blocking) I/O operation automatically, which paves a way to the upcoming {{{ngx.thread}}} API (aka the &quot;lightweight thread&quot; API).
** refactor: now we use a new Nginx subrequest model that bypasses {{{ngx_http_postpone_filter_module}}} completely, which paves a way for arbitrary order of outputting among subrequests and their parents when the {{{ngx.thread API}}} lands in the near future.
** bugfix: the &quot;http finalize non-active request&quot; alerts might happen when subrequests were used. thanks Lance Li for reporting this issue.
** bugfix: reset the subrequest status code when the {{{ngx_http_upstream}}} request in the subrequest fails due to timeout errors or premature connection close and etc. this fix also requires the [[nonbuffered-upstream-truncation patch|https://github.com/agentzh/ngx_openresty/blob/master/patches/nginx-1.2.3-nonbuffered-upstream-truncation.patch]] for the Nginx core to cancel a limitation in {{{ngx_http_upstream}}}.
** bugfix: fixed the gcc error {{{-Werror=strict-aliasing}}} in the Lua/LuaJIT bytecode loader when {{{-O2}}} or above is used. thanks jinglong for the patch.
** bugfix: the main request might be prematurely terminated if a subrequest issued by [[ngx.location.capture|http://wiki.nginx.org/HttpLuaModule#ngx.location.capture]] (or its friends) was finalized with error codes.
** bugfix: the Nginx built-in resolver might not be destroyed in time when it was used by [[ngx.socket.tcp|http://wiki.nginx.org/HttpLuaModule#ngx.socket.tcp]] and [[ngx.socket.udp|http://wiki.nginx.org/HttpLuaModule#ngx.socket.udp]].
** bugfix: [[coroutine.status()|http://wiki.nginx.org/HttpLuaModule#coroutine.status]] returned {{{suspended}}} for {{{normal}}} coroutines.
** bugfix: [[coroutine.resume()|http://wiki.nginx.org/HttpLuaModule#coroutine.resume]] did not return an error immediately when operating on {{{normal}}} coroutines.
** bugfix: when the entry coroutine was yielded by [[coroutine.yield()|http://wiki.nginx.org/HttpLuaModule#coroutine.yield]] then after it was resumed automatically its status would still be {{{suspended}}}.
** bugfix: the write event timer might not be removed in time in [[ngx.flush(true)|http://wiki.nginx.org/HttpLuaModule#ngx.flush]] when {{{ngx_handle_write_event}}} failed.
** bugfix: always remove the read event timer during downstream cosocket finalization if it is not removed yet.
** bugfix: [[ngx.flush(true)|http://wiki.nginx.org/HttpLuaModule#ngx.flush]] might not return immediately when it should.
** bugfix: the {{{resume_handler}}} field of the subrequest {{{ctx}}} was not properly initialized.
** feature: added new dtrace static probes {{{http-lua-user-coroutine-yield}}} and {{{http-lua-entry-coroutine-yield}}}.
** docs: fixed the documentation for [[ngx.req.set_header|http://wiki.nginx.org/HttpLuaModule#ngx.req.set_header]] and made it clear that the modified request headers will be inherited by the subrequests by default. thanks James Hurst for reporting this issue.
** docs: documented the trick for doing background asynchronous jobs by using [[ngx.eof()|http://wiki.nginx.org/HttpLuaModule#ngx.eof]] + {{{keepalive_timeout 0}}}. thanks Lance Li for the suggestion.
* upgraded LuaRedisParserLibrary to 0.10.
** bugfix: Lua stack overflow would happen when too many Redis arguments were passed into the [[build_query|http://wiki.nginx.org/LuaRedisParser#build_query]] method. thanks Guo Yin for reporting this issue.
* upgraded LuaRestyDNSLibrary to 0.08.
** feature: added new method [[tcp_query|https://github.com/agentzh/lua-resty-dns#tcp_query]] to enforce pure TCP transportation for the DNS queries.
** feature: added support for TCP retries when the UDP reply gets truncated.
** feature: added support for {{{PTR}}} queries and records.
** feature: added support for {{{TXT}}} queries and records.
** feature: added support for {{{NS}}} queries and records.
** bugfix: the udp resolver did not discard DNS replies with unmatched ~IDs for 128 times as originally designed.
* upgraded LuaRestyRedisLibrary to 0.14.
** optimize: now we do the string concatenation for Redis queries on the Lua land instead of on the C land, which gives 6% ~ 7% over-all performance boost when using LuaJIT 2.0 beta10.
** docs: fixed a typo in the sample code. thanks xingxing for reporting it.
* upgraded LuaRestyMemcachedLibrary to 0.08.
** feature: added new option {{{key_transform}}} to the [[new method|https://github.com/agentzh/lua-resty-memcached#new]] to allow the user to override the default escaping and unescaping methods for Memcached keys. thanks Matthieu Tourne for the patch.
** bugfix: now the [[new method|https://github.com/agentzh/lua-resty-memcached#new]] will return a string describing the error as the second return value in case of failures.
** docs: added more documentation for the [[set_keepalive|https://github.com/agentzh/lua-resty-memcached#set_keepalive]] method.
** docs: documented that this library cannot be used in those contexts where the LuaNginxModule cosocket API is unavailable.
** docs: documented that storing the object instance into Lua module-level variables will result in failures for concurrent requests.
* upgraded SrcacheNginxModule to 0.16.
** bugfix: [[srcache_fetch|http://wiki.nginx.org/HttpSRCacheModule#srcache_fetch]] would use truncated responses from MemcNginxModule or other upstream modules. this usually happened when the upstream read timer was expired or the upstream prematurely closed the connection. this fix also requires the [[nonbuffered-upsteram-truncation patch|https://github.com/agentzh/ngx_openresty/blob/master/patches/nginx-1.2.3-nonbuffered-upstream-truncation.patch]] to cancel a limitation in the Nginx core. thanks Bryan Alger for reporting the issue.
** bugfix: the main request response was not discarded by [[srcache_store|http://wiki.nginx.org/HttpSRCacheModule#srcache_store]] when there was an error in the last minute (like a read-timeout error or premature connection close happens when {{{ngx_http_upstream}}} reads the upstream response body). this fix also requires the [[nonbuffered-upstream-truncation patch|https://github.com/agentzh/ngx_openresty/blob/master/patches/nginx-1.2.3-nonbuffered-upstream-truncation.patch]] for the Nginx core to cancel a limitation in {{{ngx_http_upstream}}}.
** bugfix: the main request might prematurely terminate if the [[srcache_store|http://wiki.nginx.org/HttpSRCacheModule#srcache_store]] subrequest was finalized with error codes.
* upgraded Redis2NginxModule to 0.09.
** bugfix: directives [[redis2_query|http://wiki.nginx.org/HttpRedis2Module#redis2_query]], [[redis2_literal_raw_query|http://wiki.nginx.org/HttpRedis2Module#redis2_literal_raw_query]], and [[redis2_raw_queries|http://wiki.nginx.org/HttpRedis2Module#redis2_raw_queries]] could not be inherited automatically by the {{{location if}}} blocks, resulting in the &quot;no redis2 query specified or the query is empty&quot; error. thanks Tomasz Prus for the patch.
* feature: updated the [[dtrace patch|https://github.com/agentzh/ngx_openresty/blob/master/patches/nginx-1.2.3-dtrace.patch]] to add new static probe {{{create-pool-done}}}.
* feature: updated the [[dtrace patch|https://github.com/agentzh/ngx_openresty/blob/master/patches/nginx-1.2.3-dtrace.patch]] to include new tapset functions {{{ngx_indent}}}, {{{ngx_http_subreq_depth}}}, and {{{ngx_http_req_parent}}}.
* bugfix: added the [[nonbuffered-upstream-truncation patch|https://github.com/agentzh/ngx_openresty/blob/master/patches/nginx-1.2.3-nonbuffered-upstream-truncation.patch]] for the Nginx core to make {{{ngx_http_upstream}}} provide a way in the context of a subrequest to signal the parent of errors when upstream data truncation happens. thanks Bryan Alger for reporting this issue. (This is a temporary solution and I'll work on a new patch as per Maxim Dounin's suggestions.)
* bugfix: applied the [[channel-uninit-params patch|https://github.com/agentzh/ngx_openresty/blob/master/patches/nginx-1.2.3-channel-uninit-params.patch]] for the Nginx core to fix Valgrind/Memcheck warnings about unitialized bytes in the parameters of {{{sendmsg}}}.
* feature: updated the [[allow_request_body_updating patch|https://github.com/agentzh/ngx_openresty/blob/master/patches/nginx-1.2.3-allow_request_body_updating.patch]] to define the {{{HAVE_ALLOW_REQUEST_BODY_UPDATING_PATCH}}} macro.
! Mainline Version 1.2.3.1 - 22 August 2012
* upgraded the Nginx core to 1.2.3.
** see http://nginx.org/en/CHANGES-1.2 for changes.
* upgraded LuaNginxModule to 0.6.2.
** feature: (re)implemented the standard Lua [[coroutine API|http://wiki.nginx.org/HttpLuaModule#coroutine.create]], which means that the user is now free to create and run their own coroutines within the boilerplate coroutine created automatically by LuaNginxModule. thanks chaoslawful and jinglong for the design and implementation.
** feature: added new dtrace static probes for the user coroutine mechanism: {{{http-lua-coroutine-create}}} and {{{http-lua-coroutine-resume}}}.
** feature: added new dtrace static probes for the cosocket mechanism: {{{http-lua-socket-tcp-send-start}}}, {{{http-lua-socket-tcp-receive-done}}}, and {{{http-lua-socket-tcp-setkeepalive-buf-unread}}}.
** bugfix: the send timeout timer for downstream output was not deleted in time in our write event handler, which might result in request abortion for long running requests. thanks Demiao Lin (ldmiao) for reporting this issue.
** bugfix: [[tcpsock:send()|http://wiki.nginx.org/HttpLuaModule#tcpsock:send]] might send garbage if it was not the first call: we did not properly initialize the chain writer ctx for every {{{send()}}} call. thanks Zhu Dejiang for reporting this issue.
** bugfix: the {{{ngx_http_lua_probe.h}}} header file was not listed in the {{{NGX_ADDON_DEPS}}} list in the {{{config}}} file.
** optimize: removed unnecessary code that was for the old coroutine abortion mechanism based on Lua exceptions. we no longer need that at all because we have switched to using coroutine yield to abort the current coroutine for {{{ngx.exec}}}, {{{ngx.exit}}}, {{{ngx.redirect}}}, and {{{ngx.req.set_uri(uri, true)}}}.
* upgraded LuaRestyDNSLibrary to 0.06.
** feature: added support for MX type resource records.
** feature: unrecognized types of resource records will return their raw resource data (RDATA) as the {{{rdata}}} Lua table field.
* upgraded LuaRestyRedisLibrary to 0.13.
** feature: added new method [[read_reply|https://github.com/agentzh/lua-resty-redis#read_reply]], mostly for using the [[Redis Pub/Sub API|http://redis.io/topics/pubsub/]].
** feature: added new class method [[add_commands|https://github.com/agentzh/lua-resty-redis#add_commands]] to allow adding support for new Redis commands on-the-fly. thanks Praveen Saxena for requesting this feature.
** docs: added a code sample for using the [[Redis transactions|https://github.com/agentzh/lua-resty-redis#redis-transactions]].
* upgraded DrizzleNginxModule to 0.1.4.
** bugfix: the {{{open socket #N left in connection}}} alerts would appear in the nginx error log file when the ~MySQL/Drizzle connection pool was used and the worker process was shutting down.
* upgraded PostgresNginxModule to 1.0rc2.
** bugfix: the {{{open socket #N left in connection}}} alerts would appear in the nginx error log file when the ~PostgreSQL connection pool was used and the worker process was shutting down.
** bugfix: removed the useless http-cache related code from {{{ngx_postgres_upstream_finalize_request}}} to suppress clang warnings.
* added more dtrace static probes to the Nginx core: {{{timer-add}}}, {{{timer-del}}}, and {{{timer-expire}}}.
* added more [[systemtap|http://sourceware.org/systemtap/]] tapset functions: {{{ngx_chain_next}}}, {{{ngx_chain_writer_ctx_out}}}, {{{ngx_chain_dump}}}, and {{{ngx_iovec_dump}}}.

See ChangeLog1002001 for change log for OpenResty 1.2.1.x.
</pre>
</div>
<div title="ChangeLog1002004" creator="YichunZhang" modifier="YichunZhang" created="201210142036" modified="201212232252" changecount="13">
<pre>&lt;&lt;toolbar permalink&gt;&gt;
! Stable Release 1.2.4.14 - 23 December 2012
* upgraded LuaNginxModule to 0.7.9.
** bugfix: assignment to [[ngx.status|http://wiki.nginx.org/HttpLuaModule#ngx.status]] would always be overridden by the later [[ngx.exit()|http://wiki.nginx.org/HttpLuaModule#ngx.exit]] calls for HTTP 1.0 requests if [[lua_http10_buffering|http://wiki.nginx.org/HttpLuaModule#lua_http10_buffering]] is on (the default setting). thanks chenshu for reporting this issue.
** bugfix: there was a typo in the error message when accessing an Nginx variable that has not been defined.
** docs: documented the request body automatic inheritance behaviour in [[ngx.location.capture|http://wiki.nginx.org/HttpLuaModule#ngx.location.capture]].
** docs: fixed incorrect dates shown in the code samples for [[ngx.http_time|http://wiki.nginx.org/HttpLuaModule#ngx.http_time]] and [[ngx.parse_http_time|http://wiki.nginx.org/HttpLuaModule#ngx.parse_http_time]]. thanks Gosuke Miyashita for the patch.
* upgraded LuaRestyUploadLibrary to 0.05.
** bugfix: unexpected runtime exceptions would be thrown when {{{resty.upload}}} met a in-part header field line or a terminating boundary line that was too long. this bug had appeared in LuaRestyUploadLibrary 0.04 and OpenResty 1.2.4.7.
** bugfix: {{{resty.upload}}} could not parse {{{Content-Type}}} request header values like {{{boundary=&quot;simple boundary&quot;}}}, that is, with double quotes around the boundary value.
** optimize: marked internal auxiliary functions as Lua {{{local}}} functions.
The following components are bundled:
* ~LuaJIT-2.0.0
* array-var-nginx-module-0.03rc1
* auth-request-nginx-module-0.2
* drizzle-nginx-module-0.1.4
* echo-nginx-module-0.41
* encrypted-session-nginx-module-0.02
* form-input-nginx-module-0.07rc5
* headers-more-nginx-module-0.19
* iconv-nginx-module-0.10rc7
* lua-5.1.5
* lua-cjson-1.0.3
* lua-rds-parser-0.05
* lua-redis-parser-0.10
* lua-resty-dns-0.09
* lua-resty-memcached-0.10
* lua-resty-mysql-0.12
* lua-resty-redis-0.15
* lua-resty-string-0.08
* lua-resty-upload-0.05
* memc-nginx-module-0.13rc3
* nginx-1.2.4
* ngx_coolkit-0.2rc1
* ngx_devel_kit-0.2.17
* ngx_lua-0.7.9
* ngx_postgres-1.0rc2
* rds-csv-nginx-module-0.05rc2
* rds-json-nginx-module-0.12rc10
* redis-nginx-module-0.3.6
* redis2-nginx-module-0.09
* set-misc-nginx-module-0.22rc8
* srcache-nginx-module-0.16
* xss-nginx-module-0.03rc9
! Mainline Version 1.2.4.13 - 11 December 2012
* upgraded LuaNginxModule to 0.7.8.
** bugfix: [[ngx.req.set_body_file()|http://wiki.nginx.org/HttpLuaModule#ngx.req.set_body_file]] might lead to memory issues because it directly used the storage of Lua strings allocated by the Lua GC (we should have allocated a new memory block on the Nginx side and copy the string data over).
* upgraded LuaRestyMySQLLibrary to 0.12.
** feature: convert the ~MySQL {{{newdecimal}}} typed fields to Lua numbers by default as requested by shedar.
** optimize: marked the internal Lua function {{{_recv_packet}}} as a {{{local}}} function.
! Mainline Version 1.2.4.11 - 8 December 2012
* upgraded LuaNginxModule to 0.7.7.
** feature: added [[ngx.req.start_time()|http://wiki.nginx.org/HttpLuaModule#ngx.req.start_time]] to return the request starting time in seconds (the milliseconds part is the decimal part just as in [[ngx.now|http://wiki.nginx.org/HttpLuaModule#ngx.now]]). thanks Matthieu Tourne for the patch.
** feature: setting [[ngx.status|http://wiki.nginx.org/HttpLuaModule#ngx.status]] or calling [[ngx.exit(N)|http://wiki.nginx.org/HttpLuaModule#ngx.exit]] (where {{{N &gt;= 300}}}) after sending out response headers no longer yields a Lua exception but only leaves an error message in the error.log file, which is useful for Lua land debugging. thanks Matthieu Tourne for requesting this.
** feature: the user can now call [[ngx.exit(444)|http://wiki.nginx.org/HttpLuaModule#ngx.exit]] to abort pending subrequests in other &quot;light threads&quot; from within a &quot;light thread&quot;.
** feature: added new dtrace static probe {{{http-lua-user-thread-wait}}}.
** bugfix: [[ngx.location.capture|http://wiki.nginx.org/HttpLuaModule#ngx.location.capture]] and [[ngx.location.capture_multi|http://wiki.nginx.org/HttpLuaModule#ngx.location.capture_multi]] might hang infinitely because the parent request might not be waken up right after the first time the {{{post_subrequest}}} callback was called.
** bugfix: the &quot;light thread&quot; object created by [[ngx.thread.spawn()|http://wiki.nginx.org/HttpLuaModule#ngx.thread.spawn]] or [[ngx.on_abort()|http://wiki.nginx.org/HttpLuaModule#ngx.on_abort]] might be prematurely collected by the Lua GC because we did not correctly register its coroutine object into the Lua regsitry table. this bug may crash the Lua VM and Nginx workers under load. thanks Zhu Dejiang for reporting this issue.
** bugfix: [[ngx.thread.wait()|http://wiki.nginx.org/HttpLuaModule#ngx.thread.wait]] might hang infinitely when more than 4 user &quot;light threads&quot; are created in the same request handler due to the incorrect use of {{{ngx_array_t}}} for {{{ngx_list_t}}}. thanks Junwei Shi for reporting this issue.
** bugfix: when a user coroutine or user &quot;light thread&quot; dies with an error, our Lua backtrace dumper written in C may access one of its dead parent threads (if any) which could lead to segmentation faults.
** bugfix: [[ngx.exit(N)|http://wiki.nginx.org/HttpLuaModule#ngx.exit]] incorrectly threw out Lua exceptions when {{{N}}} was 408, 499, or 444 and the response header was already sent. thanks Kindy Lin for reporting this issue.
** bugfix: when the user callback function registered by [[ngx.on_abort()|http://wiki.nginx.org/HttpLuaModule#ngx.on_abort]] discarded the client abort event, the request would be aborted by force when the next client abort event happened.
** bugfix: an English typo in the error message for [[init_by_lua*|http://wiki.nginx.org/HttpLuaModule#init_by_lua]].
* applied [[slab_alloc_no_memory_as_info.patch|https://github.com/agentzh/ngx_openresty/blob/master/patches/nginx-1.2.4-slab_alloc_no_memory_as_info.patch]] to lower the log level of the error message &quot;ngx_slab_alloc() failed: no memory&quot; from &quot;crit&quot; to &quot;info&quot;.
* bugfix: the [[upstream_pipelining patch|https://github.com/agentzh/ngx_openresty/blob/master/patches/nginx-1.2.4-upstream_pipelining.patch]] introduced a regression that when {{{upstream_next}}} is in action, Nginx might hang. thanks Kindy Lin for reporting this issue.
* bugfix: include the latest changes in the LuaJIT 2.0 git repository (up to git commit 2ad9834d).
! Mainline Version 1.2.4.9 - 20 November 2012
* upgraded LuaJIT to 2.0.0 final.
** change logs: http://luajit.org/changes.html
* upgraded LuaNginxModule to 0.7.5.
** bugfix: [[ngx.req.clear_header()|http://wiki.nginx.org/HttpLuaModule#ngx.req.clear_header]] would result in memory invalid reads when removing the 21st, 41st, 61st (and etc) request headers. thanks Umesh Sirsiwal for reporting this issue.
** bugfix: [[ngx.log()|http://wiki.nginx.org/HttpLuaModule#ngx.log]] would truncate the log messages which have null characters ({{{\0}}}) in it. thanks Wang Xi for reporting this issue.
** docs: eliminated the use of {{{package.seeall}}} in code samples and also explicitly discouraged the use of it.
** docs: documented the special case that client closes the connection before [[ngx.req.socket()|http://wiki.nginx.org/HttpLuaModule#ngx.req.socket]] finishes reading the whole body.
* upgraded HeadersMoreNginxModule to 0.19.
**  bugfix: [[more_clear_input_headers|http://wiki.nginx.org/HttpHeadersMoreModule#more_clear_input_headers]] would result in memory invalid reads when removing the 21st, 41st, 61st (and etc) request headers. thanks Umesh Sirsiwal for reporting this issue.
** docs: fixed an issue in the sample code that tried to clear {{{Transfer-Encoding}}} which cannot actually be cleared. thanks koukou73gr.
* upgraded LuaRestyStringLibrary to 0.08.
** bugfix: the {{{new()}}} method in the {{{resty.aes}}} module might use a random key when the {{{method}}} option is omitted in the {{{hash}}} table argument. thanks wsser for the patch.
** feature: we now return a second string describing the error when either {{{iv}}} or {{{key}}} is bad.
* bugfix: {{{./configure --with-pcre=PATH}}} did not accept relative paths as {{{PATH}}}. thanks smallfish for reporting this issue.
! Mainline Version 1.2.4.7 - 11 November 2012
* upgraded LuaJIT to 2.0.0rc3.
* upgraded LuaNginxModule to 0.7.4.
** feature: added new directive [[lua_check_client_abort|http://wiki.nginx.org/HttpLuaModule#lua_check_client_abort]] (default to {{{off}}}) for monitoring and processing the event that the client closes the (downstream) connection prematurely. thanks Zhu Dejiang for request this feature.
** feature: added new Lua API [[ngx.on_abort()|http://wiki.nginx.org/HttpLuaModule#ngx.on_abort]] which is used to register user Lua function callback for the event that the client closes the (downstream) connection prematurely. thanks Matthieu Tourne for suggesting this feature.
** feature: [[ngx.exit|http://wiki.nginx.org/HttpLuaModule#ngx.exit]](N) can now abort pending subrequests when {{{N = 408}}} (request time out) or {{{N = 499}}} (client closed request) or {{{N = -1}}} (error).
** bugfix: The TCP/stream cosocket's [[connect()|http://wiki.nginx.org/HttpLuaModule#tcpsock:connect]] method could not detect errors like &quot;connection refused&quot; when kqueue was used (on ~FreeBSD or Mac OS X, for example). thanks smallfish for reporting this issue.
** bugfix: reading operations on [[ngx.req.socket()|http://wiki.nginx.org/HttpLuaModule#ngx.req.socket]] did not return any errors when the request body got truncated; now we return the &quot;client aborted&quot; error.
* upgraded LuaRestyDNSLibrary to 0.09.
** refactor: avoided using {{{package.seeall}}} in Lua module definitions, which improves performance and also prevents subtle bad side-effects.
** bugfix: a debugging output might be sent to stdout unexpectedly in some code path.
* upgraded LuaRestyMemcachedLibrary to 0.10.
** refactor: avoided using {{{package.seeall}}} in Lua module definitions, which improves performance and also prevents subtle bad side-effects.
** docs: fixed typos in README. thanks cyberty for the patch.
* upgraded LuaRestyRedisLibrary to 0.15.
** refactor: avoided using {{{package.seeall}}} in Lua module definitions, which improves performance and also prevents subtle bad side-effects.
** optimize: avoided using {{{ipairs()}}} which is slower than plain {{{for i=1,N}}} loops.
* upgraded LuaRestyMySQLLibrary to 0.11.
** refactor: avoided using {{{package.seeall}}} in Lua module definitions, which improves performance and also prevents subtle bad side-effects.
** feature: now the [[new()|https://github.com/agentzh/lua-resty-mysql#new]] method will return a string describing the error as the second return value in case of failures.
* upgraded LuaRestyUploadLibrary to 0.04.
** refactor: avoided using {{{package.seeall}}} in Lua module definitions, which improves performance and also prevents subtle bad side-effects.
* upgraded LuaRestyStringLibrary to 0.07.
** refactor: avoided using {{{package.seeall}}} in Lua module definitions, which improves performance and also prevents subtle bad side-effects.
** docs: typo-fixes in the code samples from Bearnard Hibbins.
* bugfix: nginx upstream modules could not detect the &quot;connection refused&quot; error in time if kqueue was used; now we apply the [[upstream_test_connect_kqueue patch|https://github.com/agentzh/ngx_openresty/blob/master/patches/nginx-1.2.4-upstream_test_connect_kqueue.patch]] for the Nginx core.
! Mainline Version 1.2.4.5 - 30 October 2012
* applied the official [[hotfix #1 patch|http://luajit.org/download/beta11_hotfix1.patch]] to LuaJIT 2.0.0 beta11.
** see details here: http://www.freelists.org/post/luajit/Hotfix1-for-LuaJIT200beta11
* upgraded LuaNginxModule to 0.7.3.
** feature: added the [[get_keys|http://wiki.nginx.org/HttpLuaModule#ngx.shared.DICT.get_keys]] method for the shared memory dictionaries for fetching all the (or the specified number of) keys (default to 1024 keys). thanks Brian Akins for the patch.
! Mainline Version 1.2.4.3 - 17 October 2012
* upgraded LuaJIT to 2.0.0 beta11.
** made LuaRestyRedisLibrary 27% faster, LuaRestyMemcachedLibrary 22% faster, and LuaRestyMySQLLibrary 15% faster, all for simple test cases loaded by ab, tested on Linux x86_64.
** all Lua ~APIs involved with I/O in LuaNginxModule are faster in general.
** complete change log: http://luajit.org/changes.html
* upgraded LuaRestyMemcachedLibrary to 0.09.
** optimize: we now use Lua's own {{{table.concat()}}} to do string concatenation for all the memcached requests instead of relying on the [[cosocket API|http://wiki.nginx.org/HttpLuaModule#tcpsock:send]] (on the C level) because calling the Lua C API is much slower especially when LuaJIT is in use. now for simple test cases loaded by {{{ab -k -c10}}}, we get 11.3% overall performance boost.
* upgraded LuaNginxModule to 0.7.2.
** feature: now we can automatically detect the vendor-provided ~LuaJIT-2.0 package on Gentoo. thanks Il'ya V. Yesin for the patch. it is still recommended, however, to explicitly set the environments {{{LUAJIT_INC}}} and {{{LUAJIT_LIB}}}.
! Mainline Version 1.2.4.1 - 14 October 2012
* upgraded the Nginx core to 1.2.4.
** see http://nginx.org/en/CHANGES-1.2 for changes.
* upgraded LuaNginxModule to 0.7.1.
** feature: implemented the &quot;light threads&quot; API, which allows asynchronous concurrent processing within a single Nginx request handler, based on automatically-scheduled Lua coroutines. thanks Lee Holloway for requesting this feature.
*** http://wiki.nginx.org/HttpLuaModule#ngx.thread.spawn
*** http://wiki.nginx.org/HttpLuaModule#ngx.thread.wait
** bugfix: [[ngx.re.gsub()|http://wiki.nginx.org/HttpLuaModule#ngx.re.gsub]] might throw out the exception {{{attempt to call a string value}}} when the {{{replace}}} argument was a Lua function and the subject string was large. thanks Zhu Maohai for reporting this issue.
** bugfix: older gcc versions might issue warnings like {{{variable 'nrets' might be clobbered by 'longjmp' or 'vfork'}}}, like gcc 3.4.3 (for Solaris 11) and gcc 4.1.2 (for Red Hat Linux). thanks Wenhua Zhang for reporting this issue.
** docs: added a warning for [[ngx.var.VARIABLE|http://wiki.nginx.org/HttpLuaModule#ngx.var.VARIABLE]] that memory is allocated in the per-request memory pool. thanks lilydjwg.
** docs: made it clear why {{{return}}} is recommended to be used with [[ngx.exit()|http://wiki.nginx.org/HttpLuaModule#ngx.exit]]. thanks Antoine.
** docs: massive wording improvements from Dayo.
* now we add SrcacheNginxModule before both LuaNginxModule and HeadersMoreNginxModule so that the former's output filter runs //after// those of the latter.

See ChangeLog1002003 for change log for OpenResty 1.2.3.x.</pre>
</div>
<div title="ChangeLog1002006" creator="YichunZhang" modifier="YichunZhang" created="201301050732" modified="201302180410" changecount="5">
<pre>&lt;&lt;toolbar permalink&gt;&gt;
! Mainline Version 1.2.6.6 - 17 February 2013
* upgraded LuaNginxModule to 0.7.15.
** bugfix: the original Lua VM error messages might get lost in case of Lua code crashes when user coroutines were used. thanks Dirk Feytons for the report.
** diagnose: added more info about {{{r-&gt;main-&gt;count}}} to the debugging logs.
** style: massive coding style fixes according to the Nginx coding style.
The following components are bundled:
* ~LuaJIT-2.0.0
* array-var-nginx-module-0.03rc1
* auth-request-nginx-module-0.2
* drizzle-nginx-module-0.1.4
* echo-nginx-module-0.42
* encrypted-session-nginx-module-0.02
* form-input-nginx-module-0.07
* headers-more-nginx-module-0.19
* iconv-nginx-module-0.10rc7
* lua-5.1.5
* lua-cjson-1.0.3
* lua-rds-parser-0.05
* lua-redis-parser-0.10
* lua-resty-dns-0.09
* lua-resty-memcached-0.10
* lua-resty-mysql-0.12
* lua-resty-redis-0.15
* lua-resty-string-0.08
* lua-resty-upload-0.07
* memc-nginx-module-0.13rc3
* nginx-1.2.6
* ngx_coolkit-0.2rc1
* ngx_devel_kit-0.2.18
* ngx_lua-0.7.15
* ngx_postgres-1.0rc2
* rds-csv-nginx-module-0.05rc2
* rds-json-nginx-module-0.12rc10
* redis-nginx-module-0.3.6
* redis2-nginx-module-0.09
* set-misc-nginx-module-0.22rc8
* srcache-nginx-module-0.19
* xss-nginx-module-0.03rc9
! Mainline Version 1.2.6.5 - 8 February 2013
* upgraded SrcacheNginxModule to 0.19.
** bugfix: HEAD and conditional GET requests would still fall back to content handler execution (leading to backend accesses) even in case of a cache hit. thanks Wang Lichao for reporting this issue.
** style: massive coding style fixes.
* upgraded LuaRestyUploadLibrary to 0.07.
** bugfix: the boundary string could not be parsed if no space was present before the {{{boundary=xxx}}} parameter in the {{{Content-Type}}} request header. thanks chenshu for reporting this issue.
! Mainline Version 1.2.6.3 - 3 February 2013
* upgraded LuaNginxModule to 0.7.14.
** feature: implemented named subpattern support in [[ngx.re.match|http://wiki.nginx.org/HttpLuaModule#ngx.re.match]], [[ngx.re.gmatch|http://wiki.nginx.org/HttpLuaModule#ngx.re.gmatch]], [[ngx.re.sub|http://wiki.nginx.org/HttpLuaModule#ngx.re.sub]], and [[ngx.re.gsub|http://wiki.nginx.org/HttpLuaModule#ngx.re.gsub]]; also added new regex option {{{D}}} to allow duplicate named subpattern names. thanks Ray Bejjani for the patch.
** feature: implemented the {{{J}}} regex option for the PCRE Javascript compatible mode in the [[ngx.re API|http://wiki.nginx.org/HttpLuaModule#ngx.re.match]]. thanks lhmwzy for requesting this.
** feature: setting [[ngx.header.HEADER|http://wiki.nginx.org/HttpLuaModule#ngx.header.HEADER]] after sending out the response headers now only produced an error message in the Nginx error logs and does not throw out a Lua exception. this should be handy for Lua development. thanks Matthieu Tourne for requesting this.
** feature: automatic Lua 5.1 interpreter detection on ~OpenBSD 5.2. thanks Ilya Shipitsin for the patch.
** refactor: when the Nginx core fails to send the &quot;100 Continue&quot; response in case of the &quot;Expect: 100-continue&quot; request header (or just running out of memory), [[ngx.req.read_body()|http://wiki.nginx.org/HttpLuaModule#ngx.req.read_body]] will no longer throw out a &quot;failed to read request body&quot; Lua error but will just terminate the current request and returns the 500 error page immediately, just as what the Nginx core currently does in this case.
** bugfix: because of the recent API behaviour changes in nginx 1.2.6+ and 1.3.9+, the &quot;http request count is zero&quot; alert might happen when [[ngx.req.read_body()|http://wiki.nginx.org/HttpLuaModule#ngx.req.read_body]] was called to read the request body and Nginx failed to send out the &quot;100 Continue&quot; response (like client connection early abortion and etc). thanks stonehuzhan for reporting this issue.
** bugfix: setting the &quot;eof&quot; argument (i.e., {{{ngx.arg[2]}}}) in [[body_filter_by_lua*|http://wiki.nginx.org/HttpLuaModule#body_filter_by_lua]] for a subrequest could truncate the main request's response data stream.
** bugfix: in [[body_filter_by_lua*|http://wiki.nginx.org/HttpLuaModule#body_filter_by_lua]], the &quot;eof&quot; argument (i.e., {{{ngx.arg[2]}}}) was never set in Nginx subrequests.
** bugfix: for nginx 1.3.9+ compatibility, we return an error while using [[ngx.req.socket()|http://wiki.nginx.org/HttpLuaModule#ngx.req.socket]] to read the chunked request body (for now), because chunked support in the downstream cosocket API is still a TODO.
** bugfix: for nginx 1.3.9+ compatibility, [[rewrite_by_lua*|http://wiki.nginx.org/HttpLuaModule#rewrite_by_lua]] or [[access_by_lua*|http://wiki.nginx.org/HttpLuaModule#access_by_lua]] handlers might hang if the request body was read there, because the Nginx core now overwrites {{{r-&gt;write_event_handler}}} to {{{ngx_http_request_empty_handler}}} in its {{{ngx_http_read_client_request_body}}} API.
** bugfix: for nginx 1.3.9+ compatibility, we now throw an error in [[ngx.req.init_body()|http://wiki.nginx.org/HttpLuaModule#ngx.req.init_body]], [[ngx.req.set_body_data()|http://wiki.nginx.org/HttpLuaModule#ngx.req.set_body_data]], and [[ngx.req.set_body_file()|http://wiki.nginx.org/HttpLuaModule#ngx.req.set_body_file]] when calling them without calling [[ngx.req.read_body()|http://wiki.nginx.org/HttpLuaModule#ngx.req.read_body]] or after calling [[ngx.req.discard_body()|http://wiki.nginx.org/HttpLuaModule#ngx.req.discard_body]].
** bugfix: a compilation error would happen when building with an Nginx core patched by the SPDY patch 58_1.3.11 because the patch had removed a request field from the Nginx core. thanks Chris Lea for reporting this.
** bugfix: we did not get the request reference counter (i.e., {{{r-&gt;main-&gt;count}}}) right when [[lua_need_request_body|http://wiki.nginx.org/HttpLuaModule#lua_need_request_body]] was turned on and nginx versions older than 1.2.6 or 1.2.9 were used.
** optimize: we no longer traverse the captured body chain everytime we append a new link to it in [[ngx.location.capture|http://wiki.nginx.org/HttpLuaModule#ngx.location.capture]] and [[ngx.location.capture_multi|http://wiki.nginx.org/HttpLuaModule#ngx.location.capture_multi]].
** docs: documented the [[ngx.quote_sql_str|http://wiki.nginx.org/HttpLuaModule#ngx.quote_sql_str]] API.
* upgraded SrcacheNginxModule to 0.18.
** bugfix: we might serve a truncated [[srcache_fetch|http://wiki.nginx.org/HttpSRCacheModule#srcache_fetch]] subrequest's response body as the cached response.
* upgraded EchoNginxModule to 0.42.
** feature: the [[echo_after_body|http://wiki.nginx.org/HttpEchoModule#echo_after_body]] directive is now enabled in Nginx subrequests (again).
** bugfix: we did not set the &quot;last_in_chain&quot; buffer flag when [[echo_after_body|http://wiki.nginx.org/HttpEchoModule#echo_after_body]] was used in subrequests.
* upgraded FormInputNginxModule to 0.07.
** bugfix: Nginx might hang when it failed to send the &quot;100 Continue&quot; response for Nginx versions older than 1.2.6 (and those older than 1.3.9 in the 1.3.x series).
* upgraded NginxDevelKit ot 0.2.18.
** bugfix: various fixes for C89 compliance. also stripped some line-trailing spaces.
** bugfix: guard macros were missing in the {{{ndk_set_var.h}}} header file.
** bugfix: the {{{ndk_string}}} submodule failed to compile with gcc 4.6. thanks Jon Kolb for the patch.
** bugfix: the {{{ndk_set_var}}} example did not use the new way in its {{{config}}} file. thanks Amos Wenger for the patch.
** docs: fixes in README to reflect recent changes. thanks Amos Wenger for the patch.
* applied Ruslan Ermilov's [[resolver_wev_handler_segfault_with_poll patch|https://github.com/agentzh/ngx_openresty/blob/master/patches/nginx-1.2.6-resolver_wev_handler_segfault_with_poll.patch]] to the Nginx core bundled. see [[the related nginx-devel thread|http://mailman.nginx.org/pipermail/nginx-devel/2013-January/003275.html]] for details.
* excluded the [[allow_request_body_updating patch|https://github.com/agentzh/ngx_openresty/blob/master/patches/nginx-1.2.5-allow_request_body_updating.patch]] from the Nginx core bundled.
! Mainline Version 1.2.6.1 - 4 January 2013
* upgraded the Nginx core to 1.2.6.
** see http://nginx.org/en/CHANGES-1.2 for changes.
* upgraded LuaNginxModule to 0.7.13.
** bugfix: [[ngx.decode_args()|http://wiki.nginx.org/HttpLuaModule#ngx.decode_args]] might result in Lua string storage corruption. thanks Xu Jian for the report and Kindy Lin for the patch.
** bugfix: using a key with underscores in [[ngx.header.KEY|http://wiki.nginx.org/HttpLuaModule#ngx.header.HEADER]] resulted in Lua string storage corruption. thanks rkearsley for reporting this issue.
** bugfix: accessing [[ngx.var.VARIABLE|http://wiki.nginx.org/HttpLuaModule#ngx.var.VARIABLE]] allocated temporary memory buffers in the request memory pool, which could lead to unnecessarily large memory footprint; now it allocates such buffers via Lua GC.
** feature: automatically detect LuaJIT 2.0 on ~FreeBSD by default. thanks rkearsley for the patch.
** docs: explained why {{{local foo = require &quot;foo&quot;}}} is required for loading a Lua module. thanks rkearsley for asking.
** docs: fixed a typo in the code sample for [[tcpsock:receiveuntil()|http://wiki.nginx.org/HttpLuaModule#tcpsock:receiveuntil]]. thanks Yecheng Fu for the patch.
** docs: fixed a typo in the Lua code sample for [[ngx.re.gmatch|http://wiki.nginx.org/HttpLuaModule#ngx.re.gmatch]] (we forgot to add {{{do}}} there). thanks Guo Yin for reporting this issue.
* upgraded LuaRestyUploadLibrary to 0.06.
** optimize: use the pure lower-case form of the key {{{content-type}}} to index the headers table returned by [[ngx.req.get_headers()|http://wiki.nginx.org/HttpLuaModule#ngx.req.get_headers]] so as to avoid the overhead of calling the {{{__index}}} metamethod.
* upgraded SrcacheNginxModule to 0.17.
** bugfix: [[srcache_store|http://wiki.nginx.org/HttpSRCacheModule#srcache_store]] would emit the misleading error message &quot;srcache_store: skipped because response body truncated: N &gt; 0&quot; for HEAD requests (because a HEAD request's response never carries a body); now it just skips such responses silently. thanks Yang Jin for reporting this issue.
* bugfix: when relative paths were used in {{{--with-zlib=DIR}}}, {{{--with-libatomic=DIR}}}, {{{--with-md5=DIR}}}, and {{{--with-sha1=DIR}}}, the build system of Nginx could not find {{{DIR}}} at all. thanks ~LazyZhu for reporting this issue.

See ChangeLog1002004 for change log for OpenResty 1.2.4.x.</pre>
</div>
<div title="ChangeLog1002007" creator="YichunZhang" modifier="YichunZhang" created="201302230659" modified="201305131928" changecount="5">
<pre>&lt;&lt;toolbar permalink&gt;&gt;
! Stable Release 1.2.7.8 - 13 May 2013
* applied the official patch for the nginx core to address the recent nginx security vulnerability ~CVE-2013-2070.
! Stable Release 1.2.7.6 - 17 April 2013
* upgraded LuaNginxModule to 0.7.21.
** bugfix: boolean values in an array table were rejected with the exception &quot;attempt to use boolean as query arg value&quot; while encoding a Lua (hash) table as URL arguments. thanks Calin Don for reporting this issue.
** bugfix: [[ngx.req.raw_header()|http://wiki.nginx.org/HttpLuaModule#ngx.req.raw_header]] would return an empty string value when the default header buffer ({{{c-&gt;buffer}}}) can hold the request line but not the whole header. thanks ~KDr2 for reporting this issue.
* upgraded EncryptedSessionNginxModule to 0.03.
** refactor: fixed typos in the source code: replacing &quot;3des&quot; with &quot;aes&quot;; thanks Edgar Liu for reporting this issue.
* upgraded IconvNginxModule to 0.10.
** bugfix: failed to build on Solaris with the bogus error message &quot;ngx_devel_kit is required to build ngx_iconv; please put it before ngx_iconv&quot;.
The following components are bundled:
* ~LuaJIT-2.0.1 (with hotfix #1)
* array-var-nginx-module-0.03rc1
* auth-request-nginx-module-0.2
* drizzle-nginx-module-0.1.5
* echo-nginx-module-0.45
* encrypted-session-nginx-module-0.03
* form-input-nginx-module-0.07
* headers-more-nginx-module-0.19
* iconv-nginx-module-0.10
* lua-5.1.5
* lua-cjson-1.0.3
* lua-rds-parser-0.05
* lua-redis-parser-0.10
* lua-resty-dns-0.09
* lua-resty-memcached-0.11
* lua-resty-mysql-0.13
* lua-resty-redis-0.15
* lua-resty-string-0.08
* lua-resty-upload-0.08
* memc-nginx-module-0.13rc3
* nginx-1.2.7
* ngx_coolkit-0.2rc1
* ngx_devel_kit-0.2.18
* ngx_lua-0.7.21
* ngx_postgres-1.0rc2
* rds-csv-nginx-module-0.05rc2
* rds-json-nginx-module-0.12rc10
* redis-nginx-module-0.3.6
* redis2-nginx-module-0.10
* set-misc-nginx-module-0.22rc8
* srcache-nginx-module-0.19
* xss-nginx-module-0.03rc9
! Mainline Version 1.2.7.5 - 9 April 2013
* upgraded EchoNginxModule to 0.45.
** bugfix: [[$echo_client_request_headers|http://wiki.nginx.org/HttpEchoModule#.24echo_client_request_headers]] would return the first part of the request body when request body was read before reading this variable.
** bugfix: [[$echo_client_request_headers|http://wiki.nginx.org/HttpEchoModule#.24echo_client_request_headers]] might not work properly in a subrequest.
* upgraded DrizzleNginxModule to 0.1.5.
** bugfix: compilation errors occurred with nginx 1.3.15. thanks Karl Blessing for reporting this issue.
** docs: fixed a typo in the sample code for [[$drizzle_thread_id|http://wiki.nginx.org/HttpDrizzleModule#.24drizzle_thread_id]] reported by 岚偑/yy秋叶.
** docs: documented the config syntax for db passwords with special chars in them.
* upgraded LuaNginxModule to 0.7.20.
** feature: now we allow the &quot;0&quot; time argument in [[ngx.sleep()|http://wiki.nginx.org/HttpLuaModule#ngx.sleep]].
** feature: [[ngx.location.capture|http://wiki.nginx.org/HttpLuaModule#ngx.location.capture]] and [[ngx.location.capture_multi|http://wiki.nginx.org/HttpLuaModule#ngx.location.capture_multi]] now return a lua table with the boolean field &quot;truncated&quot;, which indicates whether the subrequest response body is truncated.
** bugfix: request hung when rewrite cycled in [[ngx.req.set_uri(uri, true)|http://wiki.nginx.org/HttpLuaModule#ngx.req.set_uri]] instead of throwing out an error log message and a 500 page properly. thanks Calin Don for the report.
** bugfix: assignment to [[ngx.status|http://wiki.nginx.org/HttpLuaModule#ngx.status]] did not take effect when the response status line had already been generated (by ngx_proxy or others). thanks eqiuno for reporting this issue.
** bugfix: [[ngx.req.raw_header()|http://wiki.nginx.org/HttpLuaModule#ngx.req.raw_header]] would return the first part of the request body when request body was read before the call. thanks Matthieu Tourne for reporting this issue.
** bugfix: [[ngx.req.raw_header()|http://wiki.nginx.org/HttpLuaModule#ngx.req.raw_header]] might not work properly in a subrequest.
** bugfix: we would override the subrequest response status code later when error happens.
** bugfix: the debug log message &quot;lua set uri jump to &lt;uri&gt;&quot; generated by [[ngx.req.set_uri(uri, true)|http://wiki.nginx.org/HttpLuaModule#ngx.req.set_uri]] was wrong for &quot;&lt;uri&gt;&quot; was the old URI.
* upgraded LuaRestyMySQLLibrary to 0.13.
** bugfix: 64-bit integer values in the ~MySQL packets (like last insert ids) could not be properly parsed due to the lack of support for 64-bit integers in LuaJIT's standard &quot;bit&quot; module. thanks Azure Wang for the patch implementing a temporary workaround.
** docs: various typo fixes from Tor Hveem and doledoletree.
* upgraded LuaRestyMemcachedLibrary to 0.11.
** feature: added new method &quot;touch&quot; for the new Memcached command &quot;touch&quot;. thanks merlin for the patch.
* updated the [[upstream_truncation patch|https://raw.github.com/agentzh/ngx_openresty/master/patches/nginx-1.2.7-upstream_truncation.patch]] for the Nginx core.
** bugfix: chunked upstream response bodies were treated as 502. thanks Andy Yuan for the report.
** bugfix: request response status was changed to 502 after response header was sent in case of data truncation.
** bugfix: the &quot;last buf&quot; (i.e., bufs with &quot;last_buf&quot; or &quot;last_in_chain&quot; set) should not be sent downstream in case of upstream data truncation.
* updated the [[dtrace patch|https://raw.github.com/agentzh/ngx_openresty/master/patches/nginx-1.2.7-dtrace.patch]] for the Nginx core.
** feature: made the stap function {{{ngx_chain_dump()}}} print out info about the &quot;last_buf&quot; and &quot;last_in_chain&quot; flags in bufs and removed the old &quot;&lt;eof&gt;&quot; notation in the output.
! Mainline Version 1.2.7.3 - 24 March 2013
* upgraded LuaNginxModule to 0.7.18.
** feature: implemented [[ngx.req.http_version()|http://wiki.nginx.org/HttpLuaModule#ngx.req.http_version]] that returns the HTTP version number for the current request. thanks Matthieu Tourne for requesting this.
** feature: implemented the [[ngx.req.raw_header()|http://wiki.nginx.org/HttpLuaModule#ngx.req.raw_header]] function for returning the original raw HTTP protocol header string received by Nginx. thanks Matthieu Tourne for requesting this.
** feature: added new methods [[safe_set|http://wiki.nginx.org/HttpLuaModule#ngx.shared.DICT.safe_set]] and [[safe_add|http://wiki.nginx.org/HttpLuaModule#ngx.shared.DICT.safe_add]] to ngx.shared.DICT objects, which never override existing unexpired items but immediately return nil and a &quot;no memory&quot; string message when running out of storage. thanks Matthieu Tourne for requesting this.
** feature: datagram Unix domain sockets created by [[ngx.socket.udp()|http://wiki.nginx.org/HttpLuaModule#ngx.socket.udp]] can now receive data from the other endpoint via &quot;autobind&quot; on Linux. thanks Dirk Feytons for the patch.
** change: the [[ngx.re.match|http://wiki.nginx.org/HttpLuaModule#ngx.re.match]], [[ngx.re.gmatch|http://wiki.nginx.org/HttpLuaModule#ngx.re.gmatch]], [[ngx.re.sub|http://wiki.nginx.org/HttpLuaModule#ngx.re.sub]], and [[ngx.re.gsub|http://wiki.nginx.org/HttpLuaModule#ngx.re.gsub]] functions used to throw Lua exceptions aggressively for all the error conditions; now they just return an additional Lua string describing the error for almost all common errors instead of throwing exceptions, including pcre compile-time and exec-time failures. thanks Matthieu Tourne for requesting this change.
** bugfix: use of [[ngx.req.socket()|http://wiki.nginx.org/HttpLuaModule#ngx.req.socket]] could make socket reading hang infinitely when the request did not take a request body at all (that is, when the ~Content-Length request header is missing). thanks Matthieu Tourne for reporting this issue.
** bugfix: when a non-table value was specified for the &quot;args&quot; option in the [[ngx.location.capture|http://wiki.nginx.org/HttpLuaModule#ngx.location.capture]] or [[ngx.location.capture_multi|http://wiki.nginx.org/HttpLuaModule#ngx.location.capture_multi]] call, memory invalid access might happen, which resulted in garbage data at least. thanks Siddon Tang for reporting this issue.
** bugfix: when the Lua code using UDP/TCP cosockets + resolver was run in a subrequest, the subrequest could hang due to missing calls to {{{ngx_http_run_posted_requests}}} in the UDP/TCP cosocket resolver handler. thanks Lanshun Zhou for reporting this issue.
** bugfix: [[ngx.socket.udp|http://wiki.nginx.org/HttpLuaModule#ngx.socket.udp]]: memory leaks or invalid memory accesses might happen when the DNS resolver failed to resolve.
** bugfix: [[rewrite_by_lua_no_postpone|http://wiki.nginx.org/HttpLuaModule#rewrite_by_lua_no_postpone]] can only work globally and did not reject contexts like &quot;server&quot; and &quot;location&quot; configuration blocks. thanks Matthieu Tourne for reporting this issue.
** bugfix: (large) in-file request bodies could not be inherited correctly by multiple subrequests issued by [[ngx.location.capture|http://wiki.nginx.org/HttpLuaModule#ngx.location.capture]]. thanks Matthieu Tourne for reporting this issue.
** bugfix: [[ngx.req.get_headers(limit, true)|http://wiki.nginx.org/HttpLuaModule#ngx.req.get_headers]] would still return header names in the pure lower-case form when the &quot;limit&quot; argument was an integer. thanks Matthieu Tourne for reporting this issue.
** bugfix: [[ngx.re.match|http://wiki.nginx.org/HttpLuaModule#ngx.re.match]]: when the &quot;D&quot; regular expression option was specified, an empty Lua table would always be created even when the named capture was actually empty. thanks Matthieu Tourne for reporting this issue.
** docs: made it explicit that redirecting to external domains is also supported in [[ngx.redirect()|http://wiki.nginx.org/HttpLuaModule#ngx.redirect]]. thanks Ron Gomes for asking.
* upgraded EchoNginxModule to 0.44.
** bugfix: [[$echo_client_request_headers|http://wiki.nginx.org/HttpEchoModule#.24echo_client_request_headers]] was evaluated to only the last part of the request header when &quot;large header buffers&quot; were used.
** change: preserve the trailing {{{CR LF}}} at the end of the whole HTTP protocol header returned by [[$echo_client_request_headers|http://wiki.nginx.org/HttpEchoModule#.24echo_client_request_headers]].
* upgraded Redis2NginxModule to 0.10.
** feature: allow use of the request body data in Nginx variables for main requests by always reading the request body automatically; we used to always discard the request body just like the standard ngx_memcached module. thanks Ristona Hua for sharing this usage.
** docs: updated the docs for the limitations on Redis pub/sub. thanks ~LazyZhu for pointing out the potential confusions.
** docs: now we recommend LuaRestyRedisLibrary instead when being used with LuaNginxModule.
* upgraded LuaRestyUploadLibrary to 0.08.
** bugfix: when multiple {{{Content-Type}}} request headers were given, a Lua exception would be thrown; now we just pick up the first one.
** docs: better error handling in the code sample. thanks wgm.china for the report.
* feature: applied [[the variables_in_redis_pass patch|https://raw.github.com/agentzh/ngx_openresty/master/patches/ngx_http_redis-0.3.6-variables_in_redis_pass.patch]] to RedisNginxModule 0.3.6 to allow use of Nginx variables in the [[redis_pass|http://wiki.nginx.org/HttpRedisModule#redis_pass]] directive. thanks Diptamay Sanyal for requesting this feature.
* bugfix: applied Lanshun Zhou's [[run_posted_requests_in_resolver patch|https://raw.github.com/agentzh/ngx_openresty/master/patches/nginx-1.2.7-run_posted_requests_in_resolver.patch]] to the Nginx core: http://mailman.nginx.org/pipermail/nginx-devel/2013-March/003476.html
* bugfix:  applied the official [[hotfix #1 patch|http://luajit.org/download/v2.0.1_hotfix1.patch]] for the bundled LuaJIT 2.0.1.
! Mainline Version 1.2.7.1 - 22 February 2013
* upgraded the Nginx core to 1.2.7.
** see http://nginx.org/en/CHANGES-1.2 for changes.
* upgraded LuaJIT 2.0 to 2.0.1.
** see http://luajit.org/changes.html for changes.
* upgraded LuaNginxModule to 0.7.16.
** optimize: removed the unsed {{{size}}} field and related computatins from the script engine for the {{{ngx.re}}} API.
** optimize: saved a little memory in the script engine for the {{{ngx.re}}} API.
See ChangeLog1002006 for change log for OpenResty 1.2.6.x.</pre>
</div>
<div title="ChangeLog1002008" creator="YichunZhang" modifier="YichunZhang" created="201304270137" modified="201306101910" changecount="4">
<pre>&lt;&lt;toolbar permalink&gt;&gt;
! Stable Release 1.2.8.6 - 10 June 2013
* upgraded LuaJIT to 2.0.2.
** changes: http://luajit.org/changes.html
The following components are bundled:
* ~LuaJIT-2.0.2
* array-var-nginx-module-0.03rc1
* auth-request-nginx-module-0.2
* drizzle-nginx-module-0.1.5
* echo-nginx-module-0.45
* encrypted-session-nginx-module-0.03
* form-input-nginx-module-0.07
* headers-more-nginx-module-0.20
* iconv-nginx-module-0.10
* lua-5.1.5
* lua-cjson-1.0.3
* lua-rds-parser-0.05
* lua-redis-parser-0.10
* lua-resty-dns-0.09
* lua-resty-memcached-0.11
* lua-resty-mysql-0.13
* lua-resty-redis-0.15
* lua-resty-string-0.08
* lua-resty-upload-0.08
* memc-nginx-module-0.13rc3
* nginx-1.2.8
* ngx_coolkit-0.2rc1
* ngx_devel_kit-0.2.18
* ngx_lua-0.8.2
* ngx_postgres-1.0rc2
* rds-csv-nginx-module-0.05rc2
* rds-json-nginx-module-0.12rc10
* redis-nginx-module-0.3.6
* redis2-nginx-module-0.10
* set-misc-nginx-module-0.22rc8
* srcache-nginx-module-0.21
* xss-nginx-module-0.03rc9
! Mainline Version 1.2.8.5 - 23 May 2013
* upgraded LuaNginxModule to 0.8.2.
** feature: added {{{ngx.HTTP_MKCOL}}}, {{{ngx.HTTP_COPY}}}, {{{ngx.HTTP_MOVE}}}, and other ~WebDAV request method constants; also added corresponding support to [[ngx.req.set_method|http://wiki.nginx.org/HttpLuaModule#ngx.req.set_method]] and [[ngx.location.capture|http://wiki.nginx.org/HttpLuaModule#ngx.location.capture]]. thanks Adallom Roy for the patch.
** feature: allow injecting new user Lua ~APIs (and overriding existing Lua ~APIs) in the &quot;ngx&quot; table.
** bugfix: [[ngx.req.set_body_file()|http://wiki.nginx.org/HttpLuaModule#ngx.req.set_body_file]] always enabled Direct I/O which caused the alert message &quot;fcntl(~O_DIRECT) ... Invalid argument&quot; in error logs on file systems lacking the Direct I/O support.  thanks Matthieu Tourne for reporting this issue.
** bugfix: buffer corruption might happen in [[ngx.req.set_body_file()|http://wiki.nginx.org/HttpLuaModule#ngx.req.set_body_file]] when Nginx upstream modules were used later because [[ngx.req.set_body_file()|http://wiki.nginx.org/HttpLuaModule#ngx.req.set_body_file]] incorrectly set {{{r-&gt;request_body-&gt;buf}}} to the in-file buffer which could get reused by {{{ngx_http_upstream}}} for its own purposes.
** bugfix: no longer automatically turn underscores (_) to dashes (-) in header names for [[ngx.req.set_header|http://wiki.nginx.org/HttpLuaModule#ngx.req.set_header]] and [[ngx.req.clear_header|http://wiki.nginx.org/HttpLuaModule#ngx.req.clear_header]]. thanks aviramc for the report.
** bugfix: segmentation fault might happen in nginx 1.4.x when calling [[ngx.req.set_header|http://wiki.nginx.org/HttpLuaModule#ngx.req.set_header]] on the {{{Cookie}}} request headers because recent versions of Nginx no longer always initialize {{{r-&gt;headers_in.cookies}}}. thanks Rob W for reporting this issue.
** bugfix: fixed the C compiler warning &quot;argument 'nret' might be clobbered by 'longjmp' or 'vfork'&quot; when compiling with Ubuntu 13.04's gcc 4.7.3. thanks jacky and Rajeev's reports.
** bugfix: temporary memory leaks might happen when using [[ngx.escape_uri|http://wiki.nginx.org/HttpLuaModule#ngx.escape_uri]], [[ngx.unescape_uri|http://wiki.nginx.org/HttpLuaModule#ngx.unescape_uri]], [[ngx.quote_sql_str|http://wiki.nginx.org/HttpLuaModule#ngx.quote_sql_str]], [[ngx.decode_base64|http://wiki.nginx.org/HttpLuaModule#ngx.decode_base64]], and [[ngx.encode_base64|http://wiki.nginx.org/HttpLuaModule#ngx.encode_base64]] in tight Lua loops because we allocated memory in nginx's request memory pool for these methods.
** optimize: [[ngx.escape_uri|http://wiki.nginx.org/HttpLuaModule#ngx.escape_uri]] now runs faster when the input string contains no special bytes to be escaped.
** testing: added custom test scaffold t::~TestNginxLua which subclasses [[Test::Nginx::Socket|http://search.cpan.org/perldoc?Test%3A%3ANginx%3A%3ASocket]]. it supports the environment {{{TEST_NGINX_INIT_BY_LUA}}} which can be used to add more custom Lua code to the value of the [[init_by_lua|http://wiki.nginx.org/HttpLuaModule#init_by_lua]] directive in the Nginx configuration.
* upgraded SrcacheNginxModule to 0.21.
** bugfix: responses with a status code smaller than all the status codes specified in the [[srcache_store_statuses|http://wiki.nginx.org/HttpSRCacheModule#srcache_store_statuses]] directive were not skipped as expected. thanks Lanshun Zhou for the patch.
* feature: applied the [[invalid_referer_hash patch|https://raw.github.com/agentzh/ngx_openresty/master/patches/nginx-1.2.8-invalid_referer_hash.patch]] to the Nginx core to make the {{{$invalid_referer}}} variable accessible in embedded dynamic languages like Perl and Lua. thanks Fry-kun for requesting this.
* updated the [[dtrace patch|https://raw.github.com/agentzh/ngx_openresty/master/patches/nginx-1.2.8-dtrace.patch]] for the Nginx core.
** print out more info about the Nginx in-file bufs in the tapset function {{{ngx_chain_dump}}}.
! Mainline Version 1.2.8.3 - 13 May 2013
* applied the official patch for the nginx core to address the recent nginx security vulnerability ~CVE-2013-2070.
! Mainline Version 1.2.8.1 - 26 April 2013
* upgraded the Nginx core to 1.2.8.
** see http://nginx.org/en/CHANGES-1.2 for changes.
* upgraded LuaNginxModule to 0.8.1.
** feature: implemented the new timer API: the [[ngx.timer.at|http://wiki.nginx.org/HttpLuaModule#ngx.timer.at]] Lua function and two configure directives [[lua_max_pending_timers|http://wiki.nginx.org/HttpLuaModule#lua_max_pending_timers]] and [[lua_max_running_timers|http://wiki.nginx.org/HttpLuaModule#lua_max_running_timers]]. thanks Matthieu Tourne for requesting this feature.
** feature: added the &quot;U&quot; regex option to the [[ngx.re API|http://wiki.nginx.org/HttpLuaModule#ngx.re.match]] to mean enabling the ~UTF-8 matching mode but disabling ~UTF-8 validity check on the subject strings. thanks Lance Li for the patch.
** bugfix: setting [[ngx.header.etag|http://wiki.nginx.org/HttpLuaModule#ngx.header.HEADER]] could not affect other things reading the {{{ETag}}} response header (like the [[etag|http://nginx.org/en/docs/http/ngx_http_core_module.html#etag]] directive introduced in Nginx 1.3.3+). thanks Brian Akins for the patch.
** bugfix: when [[lua_http10_buffering|http://wiki.nginx.org/HttpLuaModule#lua_http10_buffering]] is on, for HTTP 1.0 requests, [[ngx.exit|http://wiki.nginx.org/HttpLuaModule#ngx.exit]](N) would always trigger the Nginx's own error pages when N &gt;= 300. thanks Matthieu Tourne for reporting this issue.
** bugfix: modifying the {{{Cookie}}} request headers via [[ngx.req.set_header|http://wiki.nginx.org/HttpLuaModule#ngx.req.set_header]] or [[ngx.req.clear_header|http://wiki.nginx.org/HttpLuaModule#ngx.req.clear_header]] did not update the Nginx internal data structure, {{{r-&gt;headers_in.cookies}}}, at the same time, which might cause issues when reading variables [[$cookie_COOKIE|http://wiki.nginx.org/HttpCoreModule#.24cookie_COOKIE]], for example. thanks Matthieu Tourne for the patch.
** bugfix: modifying the {{{Via}}} request header with [[ngx.req.set_header|http://wiki.nginx.org/HttpLuaModule#ngx.req.set_header]] or [[ngx.req.clear_header|http://wiki.nginx.org/HttpLuaModule#ngx.req.clear_header]] did not update the special field {{{r-&gt;headers_in.via}}} when the [[ngx_gzip|http://wiki.nginx.org/HttpGzipModule]] module was enabled.
** bugfix: modifying the {{{X-Real-IP}}} request header with [[ngx.req.set_header|http://wiki.nginx.org/HttpLuaModule#ngx.req.set_header]] or [[ngx.req.clear_header|http://wiki.nginx.org/HttpLuaModule#ngx.req.clear_header]] did not update the special field {{{r-&gt;headers_in.x_real_ip}}} when the [[ngx_realip|http://wiki.nginx.org/HttpRealipModule]] module was enabled. thanks Matthieu Tourne for the patch.
** bugfix: modifying the {{{Connection}}} request header via [[ngx.req.set_header|http://wiki.nginx.org/HttpLuaModule#ngx.req.set_header]] or [[ngx.req.clear_header|http://wiki.nginx.org/HttpLuaModule#ngx.req.clear_header]] did not update the special internal field in the Nginx core, {{{r-&gt;headers_in.connection_type}}}. Thanks Matthieu Tourne for the patch.
** bugfix: modifying the {{{User-Agent}}} request header via [[ngx.req.set_header|http://wiki.nginx.org/HttpLuaModule#ngx.req.set_header]] or [[ngx.req.clear_header|http://wiki.nginx.org/HttpLuaModule#ngx.req.clear_header]] did not update those special internal flags in the Nginx core, like {{{r-&gt;headers_in.msie6}}} and {{{r-&gt;headers_in.opera}}}. Thanks Matthieu Tourne for the patch.
** bugfix: fixed several places in the header API where we should return {{{NGX_ERROR}}} instead of {{{NGX_HTTP_INTERNAL_SERVER_ERROR}}}.
* upgraded SrcacheNginxModule to 0.20.
** bugfix: use of C global variables at the configuration phase would cause troubles when {{{HUP}}} reload failed.
* upgraded HeadersMoreNginxModule to 0.20.
** bugfix: modifying the {{{Cookie}}} request headers via [[more_set_input_headers|http://wiki.nginx.org/HttpHeadersMoreModule#more_set_input_headers]] or [[more_clear_input_headers|http://wiki.nginx.org/HttpHeadersMoreModule#more_clear_input_headers]] did not update the Nginx internal data structure, {{{r-&gt;headers_in.cookies}}}, at the same time, which might cause issues when reading variable [[$cookie_COOKIE|http://wiki.nginx.org/HttpCoreModule#.24cookie_COOKIE]], for example.
** bugfix: modifying the {{{Via}}} request header via [[more_set_input_headers|http://wiki.nginx.org/HttpHeadersMoreModule#more_set_input_headers]] or [[more_clear_input_headers|http://wiki.nginx.org/HttpHeadersMoreModule#more_clear_input_headers]] did not update the special internal field in the Nginx core, {{{r-&gt;headers_in.via}}}, when the [[ngx_gzip|http://wiki.nginx.org/HttpGzipModule]] module was enabled.
** bugfix: modifying the {{{X-Real-IP}}} request header via [[more_set_input_headers|http://wiki.nginx.org/HttpHeadersMoreModule#more_set_input_headers]] or [[more_clear_input_headers|http://wiki.nginx.org/HttpHeadersMoreModule#more_clear_input_headers]] did not update the special internal field in the Nginx core, {{{r-&gt;headers_in.x_real_ip}}}, when the [[ngx_realip|http://wiki.nginx.org/HttpRealipModule]] module was enabled.
** bugfix: modifying the {{{Connection}}} request header via [[more_set_input_headers|http://wiki.nginx.org/HttpHeadersMoreModule#more_set_input_headers]] or [[more_clear_input_headers|http://wiki.nginx.org/HttpHeadersMoreModule#more_clear_input_headers]] did not update the special internal field in the Nginx core, {{{r-&gt;headers_in.connection_type}}}.
** bugfix: modifying the {{{User-Agent}}} request header via [[more_set_input_headers|http://wiki.nginx.org/HttpHeadersMoreModule#more_set_input_headers]] or [[more_clear_input_headers|http://wiki.nginx.org/HttpHeadersMoreModule#more_clear_input_headers]] did not update those special internal flags in the Nginx core, like {{{r-&gt;headers_in.msie6}}} and {{{r-&gt;headers_in.opera}}}.
** bugfix: fixed places where we should return {{{NGX_ERROR}}} instead of {{{NGX_HTTP_INTERNAL_SERVER_ERROR}}}.
* feature: always enable debuginfo in the bundled LuaJIT 2.0.1 build and Lua 5.1.5 build to support [[Nginx Systemtap Toolkit|https://github.com/agentzh/nginx-systemtap-toolkit]].
* bugfix: no longer pass {{{-O0}}} to gcc when the {{{--with-debug}}} configure option is specified because gcc often generates bogus DWARF info when optimization is turned off.
See ChangeLog1002007 for change log for OpenResty 1.2.7.x.</pre>
</div>
<div title="ChangeLog1004001" creator="YichunZhang" modifier="YichunZhang" created="201307182312" modified="201308070331" changecount="2">
<pre>&lt;&lt;toolbar permalink&gt;&gt;
! Mainline Version 1.4.1.3 - 6 August 2013
* upgraded LuaNginxModule to 0.8.6.
** feature: added new method [[get_stale|http://wiki.nginx.org/HttpLuaModule#ngx.shared.DICT.get_stale]] to shared dict objects, which returns the value (if not freed yet) even if the key has already expired. thanks Matthieu Tourne for the patch.
** bugfix: segfaults would happen in [[ngx.req.set_header()|http://wiki.nginx.org/HttpLuaModule#ngx.req.set_header]] and [[ngx.req.clear_header()|http://wiki.nginx.org/HttpLuaModule#ngx.req.clear_header]] for HTTP 0.9 requests. thanks Bin Wang for the report.
** bugfix: segfault might happen when reading or writing to a response header via the [[ngx.header.HEADER|http://wiki.nginx.org/HttpLuaModule#ngx.header.HEADER]] API in the case that the nginx core initiated a 301 (auto) redirect. this issue was caused by an optimization in the Nginx core where {{{ngx_http_core_find_config_phase}}}, for example, does not fully initialize the &quot;Location&quot; response header after creating the header. thanks Vladimir Protasov for the report.
** bugfix: memory leak would happen when using the [[ngx.ctx|http://wiki.nginx.org/HttpLuaModule#ngx.ctx]] API before another Nginx module (other than LuaNginxModule) initiates an internal redirect.
** bugfix: use of the [[ngx.ctx|http://wiki.nginx.org/HttpLuaModule#ngx.ctx]] table in the context of [[ngx.timer|http://wiki.nginx.org/HttpLuaModule#ngx.timer.at]] callbacks would leak memory.
** bugfix: the &quot;connect() failed&quot; error message was still logged even when [[lua_socket_log_errors|http://wiki.nginx.org/HttpLuaModule#lua_socket_log_errors]] was off. thanks Dong Fang Fan for the report.
** bugfix: we incorrectly returned the 500 error code in our output header filter, body filter, and log-phase handlers upon Lua code loading errors.
** bugfix: Lua stack overflow might happen when we failed to load Lua code from the code cache.
** bugfix: the error message was misleading when the {{{*_by_lua_file}}} config directives failed to load the Lua file specified.
** bugfix: give the argument of 'void' to function definitions which has no arguments. thanks Tatsuhiko Kubo for the patch.
** bugfix: when our {{{at-panic}}} handler for Lua VM gets called, the Lua VM is not recoverable for future use. so now we try to quit the current Nginx worker gracefully so that the Nginx master can spawn a new one.
* upgraded HeadersMoreNginxModule to 0.22.
** bugfix: segfaults would happen in [[more_set_input_headers|http://wiki.nginx.org/HttpHeadersMoreModule#more_set_input_headers]] and [[more_clear_input_headers|http://wiki.nginx.org/HttpHeadersMoreModule#more_clear_input_headers]] when processing HTTP 0.9 requests. thanks Bin Wang for the patch.
** bugfix: segfault might happen when using [[more_set_headers|http://wiki.nginx.org/HttpHeadersMoreModule#more_set_headers]] or [[more_clear_headers|http://wiki.nginx.org/HttpHeadersMoreModule#more_clear_headers]] in the case that the Nginx core initiated a 301 (auto) redirect. this issue was caused by an optimization in the Nginx core where {{{ngx_http_core_find_config_phase}}}, for example, does not fully initialize the &quot;Location&quot; response header after creating the header. thanks Brian Akins for the report.
* upgraded SrcacheNginxModule to 0.22.
** bugfix: we did not always read the client request body before initiating [[srcache_fetch|http://wiki.nginx.org/HttpSRCacheModule#srcache_fetch]] subrequests at the &quot;access phase&quot;, which could lead to bad consequences.
* upgraded EchoNginxModule to 0.46.
** bugfix: the request body was not discarded properly in the content handler when the request body was not read yet. thanks Peter Sabaini for the report.
** bugfix: we did not ensure that the main request body is always read before subrequests are initiated, which could lead to bad consequences.
** bugfix: [[$echo_client_request_headers|http://wiki.nginx.org/HttpEchoModule#.24echo_client_request_headers]] may evaluate to an empty value when the default header buffer ({{{c-&gt;buffer}}}) can hold the request line but not the whole header. thanks ~KDr2 for reporting this issue.
** docs: fixed a typo in Synopsis reported by saighost.
** docs: use https for github links. thanks Olivier Mengué for the patch.
* upgraded PostgresNginxModule to 1.0rc3.
** bugfix: compilation error happened with nginx 1.5.3+ because the Nginx core changes the {{{ngx_sock_ntop}}} API. thanks an0ma1ia for the report.
! Mainline Version 1.4.1.1 - 18 July 2013
* upgraded the Nginx core to 1.4.1.
** see http://nginx.org/en/CHANGES-1.4 for changes.
* bugfix: ./configure: use of spaces in the {{{--with-cc}}} option values resulted in errors.
* bugfix: applied the [[unix_socket_accept_over_read patch|https://github.com/agentzh/ngx_openresty/blob/master/patches/nginx-1.4.1-unix_socket_accept_over_read.patch]] to fix a buffer over-read issue in  the Nginx core when Nginx is configured to listen on a unix domain socket.
* bugfix: applied the [[gcc-maybe-uninitialized-warning patch|https://github.com/agentzh/ngx_openresty/blob/master/patches/nginx-1.4.1-gcc-maybe-uninitialized-warning.patch]] to the Nginx core to fix a gcc warning with gcc 4.7.3/4.7.2.
* upgraded LuaNginxModule to 0.8.5.
** change: made [[ngx.say|http://wiki.nginx.org/HttpLuaModule#ngx.say]]/[[ngx.print|http://wiki.nginx.org/HttpLuaModule#ngx.print]]/[[ngx.eof|http://wiki.nginx.org/HttpLuaModule#ngx.eof]]/[[ngx.flush|http://wiki.nginx.org/HttpLuaModule#ngx.flush]]/[[ngx.send_headers|http://wiki.nginx.org/HttpLuaModule#ngx.send_headers]] return {{{nil}}} and a string describing the error in case of most of the common errors (instead of throwing out an exception), and return 1 for success.
** feature: added new directive [[lua_regex_match_limit|http://wiki.nginx.org/HttpLuaModule#lua_regex_match_limit]] for setting PCRE's &quot;match_limit&quot; protection for regex execution.
** feature: now we store the nginx request object as a named Lua global variable {{{__ngx_req}}} to help ~FFI-based Lua code directly access it.
** bugfix: the [[ngx.ctx|http://wiki.nginx.org/HttpLuaModule#ngx.ctx]] tables would leak memory when [[ngx.ctx|http://wiki.nginx.org/HttpLuaModule#ngx.ctx]], [[ngx.exec()|http://wiki.nginx.org/HttpLuaModule#ngx.exec]]/[[ngx.req.set_uri|http://wiki.nginx.org/HttpLuaModule#ngx.req.set_uri]](uri, true), and [[log_by_lua|http://wiki.nginx.org/HttpLuaModule#log_by_lua]] were used together in a single location. thanks Guanlan Dai for writing the gdb utils to catch this.
** bugfix: setting [[ngx.var.VARIABLE|http://wiki.nginx.org/HttpLuaModule#ngx.var.VARIABLE]] could lead to buffer over-read in {{{luaL_error}}} when an error happened.
** bugfix: [[tcpsock:send|http://wiki.nginx.org/HttpLuaModule#tcpsock:send]](&quot;&quot;) resulted in the error log alert message &quot;send() returned zero&quot;.
** bugfix: [[ngx.flush|http://wiki.nginx.org/HttpLuaModule#ngx.flush]](true) might not return 1 on success.
** bugfix: when compiling with {{{-DDDEBUG=1}}}, there was a compilation error. thanks tigeryang for the report.
** optimize: avoided use of the nginx request objects in [[ngx.escape_uri|http://wiki.nginx.org/HttpLuaModule#ngx.escape_uri]], [[ngx.unescape_uri|http://wiki.nginx.org/HttpLuaModule#ngx.unescape_uri]], [[ngx.quote_sql_str|http://wiki.nginx.org/HttpLuaModule#ngx.quote_sql_str]], [[ngx.decode_base64|http://wiki.nginx.org/HttpLuaModule#ngx.decode_base64]], [[ngx.encode_base64|http://wiki.nginx.org/HttpLuaModule#ngx.encode_base64]], [[ngx.encode_args|http://wiki.nginx.org/HttpLuaModule#ngx.encode_args]], and [[ngx.decode_args|http://wiki.nginx.org/HttpLuaModule#ngx.decode_args]].
** optimize: no longer store {{{cf-&gt;log}}} into the Lua registry table because we can always directly access the global {{{ngx_cycle-&gt;log}}} thing.
** refactor: added inline functions {{{ngx_http_lua_get_req}}} and {{{ngx_http_lua_set_req}}} to eliminate code duplication when storing or fetching the nginx request object from the lua global variable table.
** docs: typo fixes in the code sample for [[body_filter_by_lua|http://wiki.nginx.org/HttpLuaModule#body_filter_by_lua]]. thanks cyberty for the patch.
** docs: mentioned my [[Nginx Systemtap Toolkit|https://github.com/agentzh/nginx-systemtap-toolkit]] which is very useful for online debugging on Linux.
* upgraded HeadersMoreNginxModule to 0.21.
** bugfix: segmentation fault might happen in Nginx 1.4.x when using the [[more_set_input_headers|http://wiki.nginx.org/HttpHeadersMoreModule#more_set_input_headers]] directive on the Cookie request headers because recent versions of Nginx no longer always initialize {{{r-&gt;headers_in.cookies}}}.
See ChangeLog1002008 for change log for OpenResty 1.2.8.x.</pre>
</div>
<div title="ChangeLog1004002" creator="YichunZhang" modifier="YichunZhang" created="201308120420" modified="201309300657" changecount="6">
<pre>&lt;&lt;toolbar permalink&gt;&gt;
! Mainline Version 1.4.2.9 - 29 September 2013
* bundled the new LuaRestyWebSocketLibrary 0.01.
** this Lua library implements both a nonblocking ~WebSocket server and a nonblocking ~WebSocket client based on LuaNginxModule's cosocket API. thanks Hendrik Schumacher.
* bundled the new LuaRestyLockLibrary 0.01.
** this Lua library implements a simple nonblocking mutex lock API based on LuaNginxModule's shared memory dictionaries. Mostly useful for eliminating &quot;dog-pile effects&quot; and etc. thanks Sri Rao for the suggestion.
* upgraded LuaRestyRedisLibrary to 0.16.
** feature: added new redis commands bitcount, bitop, client, dump, hincrbyfloat, incrbyfloat, migrate, pexpire, pexpireat, psetex, pubsub, pttl, restore, and time. thanks alex-yam for the patch.
** optimize: eliminated the [[table.insert()|http://www.lua.org/manual/5.1/manual.html#pdf-table.insert]] calls because they are slower than {{{tb[#tb + 1] = val}}}. thanks alex-yam for the patch. this gives 1.9% speed up for trivial set and get examples when LuaJIT 2.0.2 is used and 4.9% speed up when LuaJIT's v2.1 git branch is used.
** refactor: avoided using Lua 5.1's [[module() function|http://www.lua.org/manual/5.1/manual.html#pdf-module]] for defining our Lua modules because it has bad side effects.
** docs: do not use 0 (i.e., unlimited) max idle time in the [[set_keepalive()|https://github.com/agentzh/lua-resty-redis#set_keepalive]] call in the code sample.
** docs: added code samples for the redis commands {{{hmget}}} and {{{hmset}}}. this has already become a FAQ.
** docs: added the [[Redis Authentication|https://github.com/agentzh/lua-resty-redis#redis-authentication]] section because it is already an FAQ.
** docs: documented the {{{options}}} table argument for the [[connect() method|https://github.com/agentzh/lua-resty-redis#connect]].
** docs: added a missing {{{local}}} keyword to the code sample. thanks Wendal Chen for the patch.
* upgraded LuaRestyMemcachedLibrary to 0.12.
** optimize: no longer use Lua tables and [[table.concat()|http://www.lua.org/manual/5.1/manual.html#pdf-table.concat]] to construct simple Memcached query strings. this gives 6.75% overall speed up for trivial {{{set}}} and {{{get}}} examples when LuaJIT 2.0.2 is used.
** optimize: eliminated [[table.insert()|http://www.lua.org/manual/5.1/manual.html#pdf-table.insert]] because it is slower than {{{tb[#tb + 1] = val}}}.
** refactor: avoided using Lua's [[module() function|http://www.lua.org/manual/5.1/manual.html#pdf-module]] for defining our Lua modules because it has bad side effects.
** docs: use limited (10 sec) max idel timeout for in-pool connections in the code sample.
* upgraded LuaNginxModule to 0.9.0.
** feature: added support for raw downstream cosocket via the [[ngx.req.socket(true)|http://wiki.nginx.org/HttpLuaModule#ngx.req.socket]] API, upon which http upgrading protocols like ~WebSocket can be implemented with pure Lua (see LuaRestyWebSocketLibrary). This API can also be used to bypass the Nginx output filters and emit raw HTTP response headers and/or HTTP response bodies. thanks Hendrik Schumacher and aviramc.
** bugfix: memory invalid reads might happen when [[ngx.flush(true)|http://wiki.nginx.org/HttpLuaModule#ngx.flush]] was used: the &quot;ctx&quot; struct could get freed in the middle of processing and we should save the state explicitly on the C stack.
** bugfix: the standard Lua coroutine API was not available in the context of [[init_by_lua*|http://wiki.nginx.org/HttpLuaModule#init_by_lua]] and threw out the &quot;no request found&quot; error. thanks Wolf Tivy for the report.
** bugfix: massive compatibility fixes for the Microsoft Visual C++ compiler. thanks Edwin Cleton for the report.
** bugfix: Lua VM might run out of memory when {{{lua_code_cache}}} is off; now we always enforce a full Lua GC cycle right after unloading most of the loaded Lua modules when the Lua code cache is turned off.
** change: raised the &quot;lua_code_cache is off&quot; warning to an alert.
* upgraded NginxDevelKit to 0.2.19.
** bugfix: fixed warnings from the Microsoft Visual C++ compiler. thanks Edwin Cleton for the report.
! Stable Version 1.4.2.8 - 22 September 2013
* upgraded LuaNginxModule to 0.8.10.
** bugfix: we did not declare the &quot;level&quot; local variable of {{{ngx_http_lua_ngx_log}}} at the beginning of the code block. thanks Edwin Cleton for the report.
** docs: documented more limitations in the current implementation.
** docs: avoided using [[module()|http://www.lua.org/manual/5.1/manual.html#pdf-module]] and also recommended the [[lua-releng tool|https://github.com/agentzh/nginx-devel-utils/blob/master/lua-releng]] to locate misuse of Lua globals.
The following components are bundled in this release:
* ~LuaJIT-2.0.2
* array-var-nginx-module-0.03rc1
* auth-request-nginx-module-0.2
* drizzle-nginx-module-0.1.6
* echo-nginx-module-0.48
* encrypted-session-nginx-module-0.03
* form-input-nginx-module-0.07
* headers-more-nginx-module-0.22
* iconv-nginx-module-0.10
* lua-5.1.5
* lua-cjson-1.0.3
* lua-rds-parser-0.05
* lua-redis-parser-0.10
* lua-resty-dns-0.10
* lua-resty-memcached-0.11
* lua-resty-mysql-0.13
* lua-resty-redis-0.15
* lua-resty-string-0.08
* lua-resty-upload-0.08
* memc-nginx-module-0.13
* nginx-1.4.2
* ngx_coolkit-0.2rc1
* ngx_devel_kit-0.2.18
* ngx_lua-0.8.10
* ngx_postgres-1.0rc3
* rds-csv-nginx-module-0.05rc2
* rds-json-nginx-module-0.12rc10
* redis-nginx-module-0.3.6
* redis2-nginx-module-0.10
* set-misc-nginx-module-0.22
* srcache-nginx-module-0.22
* xss-nginx-module-0.03rc9
! Mainline Version 1.4.2.7 - 15 September 2013
* upgraded LuaNginxModule to 0.8.9.
** bugfix: the Nginx core does not send a default status line for the 101 status code. now we construct one ourselves in this case.
** bugfix: nil &quot;pool&quot; option values led to errors in [[tcpsock:connect()|http://wiki.nginx.org/HttpLuaModule#tcpsock:connect]].
** bugfix: [[tcpsock:receive(0)|http://wiki.nginx.org/HttpLuaModule#tcpsock:receive]] could hang until new data arrived or the timeout error happened; now it always returns an empty string immediately. this new behaviour diverges from the [[LuaSocket library|http://w3.impa.br/~diego/software/luasocket/]] though.
** bugfix: for SPDY requests, we (temporarily) disable the Lua API functions [[ngx.location.capture|http://wiki.nginx.org/HttpLuaModule#ngx.location.capture]], [[ngx.location.capture_multi|http://wiki.nginx.org/HttpLuaModule#ngx.location.capture_multi]], and [[ngx.req.socket|http://wiki.nginx.org/HttpLuaModule#ngx.req.socket]], which are known to have problems in SPDY mode. The SPDY compatibility issue will eventually get fixed in the near future.
** refactor: removed our own {{{ctx-&gt;headers_sent}}} field because we should use Nginx core's {{{r-&gt;header_sent}}} instead.
* upgraded EchoNginxModule to 0.48.
** refactor: removed our own {{{ctx-&gt;headers_sent}}} field because we should use Nginx core's {{{r-&gt;header_sent}}} instead.
* bugfix: {{{./configure}}} now always removes existing Makefile before trying to generate a new one.
! Mainline Version 1.4.2.5 - 8 September 2013
* upgraded SetMiscNginxModule to 0.22.
** bugfix: we did not escape {{{\0}}}, {{{\z}}}, {{{\b}}}, and {{{\t}}} properly in set_quote_sql_str according to the ~MySQL quoting rules. thanks Siddon Tang for the report.
* upgraded LuaNginxModule to 0.8.8.
** feature: added new option &quot;always_forward_body&quot; to [[ngx.location.capture()|http://wiki.nginx.org/HttpLuaModule#ngx.location.capture]] and [[ngx.location.capture_multi()|http://wiki.nginx.org/HttpLuaModule#ngx.location.capture_multi]], which controls whether to always forward the parent request's request body to the subrequest (even when the subrequest is not of the POST or PUT request method). thanks Matthieu Tourne for the request.
** feature: now timeout errors in [[tcpsock:receive()|http://wiki.nginx.org/HttpLuaModule#tcpsock:receive]] and [[tcpsock:receiveuntil()|http://wiki.nginx.org/HttpLuaModule#tcpsock:receiveuntil]] no longer automatically close the current cosocket object (for both upstream and downstream connections). thanks Aviram Cohen for the original patch.
** bugfix: we did not escape {{{\0}}}, {{{\z}}}, {{{\t}}}, and {{{\b}}} properly in [[ngx.quote_sql_str()|http://wiki.nginx.org/HttpLuaModule#ngx.quote_sql_str]]. thanks Siddon Tang for the report.
** bugfix: Lua backtrace dumps upon uncaught Lua exceptions did not work with the standard Lua 5.1 interpreter when the backtrace was deeper than 22 levels.
** change: now we just dump the top 22 levels in the backtrace for uncaught Lua exceptions for the sake of simplicity.
** change: we now limit the number of nested coroutines in the backtrace dump for uncaught Lua exceptions by 5.
** optimize: grouped the Lua string concatenation operations when constructing the backtrace string for uncaught Lua exceptions.
! Mainline Version 1.4.2.3 - 2 September 2013
* upgraded LuaNginxModule to 0.8.7.
** feature: [[log_by_lua*|http://wiki.nginx.org/HttpLuaModule#log_by_lua]] now always runs before the standard [[ngx_http_log_module|http://nginx.org/en/docs/http/ngx_http_log_module.html]] (for access logging). thanks Calin Don for the suggestion.
** feature: added new API [[ngx.config.debug|http://wiki.nginx.org/HttpLuaModule#ngx.config.debug]] to indicate whether this is a debug build of Nginx (that is, being built by the {{{./configure}}} option {{{--with-debug}}}).
** bugfix: the global Lua state's {{{_G}}} table was cleared when [[lua_code_cache|http://wiki.nginx.org/HttpLuaModule#lua_code_cache]] was off, which could confuse the setup in [[init_by_lua*|http://wiki.nginx.org/HttpLuaModule#init_by_lua]]. thanks Robert Andrew Ditthardt for the report.
** bugfix: [[ngx.flush()|http://wiki.nginx.org/HttpLuaModule#ngx.flush]] triggered response header sending when the header was not sent yet. now it just returned the error string &quot;nothing to flush&quot; for this case. thanks linbo liao for the report.
** bugfix: when a Lua line comment was used in the last line of the inlined Lua code chunk, a bogus Lua syntax error would be thrown.
** bugfix: [[ngx.exit|http://wiki.nginx.org/HttpLuaModule#ngx.exit]](204) could try to send the response header twice. Nginx 1.5.4 caught this issue.
** bugfix: the error message for failures in loading inlined Lua code was misleading.
* upgraded EchoNginxModule to 0.47.
** bugfix: use of C global variables at configuration time could lead to issues when HUP reload failed in the middle.
** bugfix: we might send the response header twice when an error happens. this issue is exposed by Nginx 1.5.4. thanks Markus Linnala for the report.
* upgraded DrizzleNginxModule to 0.1.6.
** bugfix: compilation error happened with nginx 1.5.3+ because Nginx changes the {{{ngx_sock_ntop}}} API.
** docs: typo fixes from smallfish.
* upgraded MemcNginxModule to 0.13.
** bugfix: fixed compatibility issues with the new upstream C API in Nginx 1.5.3+. thanks Markus Linnala for the patch.
** bugfix: use of C global variables at configuration time could cause issues when HUP reload failed in the middle.
** docs: now we recommend LuaRestyMemcachedLibrary instead when being used with LuaNginxModule.
* applied the [[unix_socket_accept_over_read patch|https://github.com/agentzh/ngx_openresty/blob/master/patches/nginx-1.4.2-unix_socket_accept_over_read.patch]] the Nginx core to fix a memory over-read issue when Nginx was accepting a unix domain socket.
! Mainline Version 1.4.2.1 - 11 August 2013
* upgraded the Nginx core to 1.4.2.
** see http://nginx.org/en/CHANGES-1.4 for changes.
* upgraded LuaRestyDNSLibrary to 0.10.
** feature: now we return all the answer records even when the DNS server returns a non-zero error code, in which case the error code and error string are now set as the &quot;errcode&quot; and &quot;errstr&quot; fields in the Lua table returned. thanks Matthieu Tourne for requesting this.
See ChangeLog1004001 for change log for OpenResty 1.4.1.x.</pre>
</div>
<div title="ChangeLog1004003" creator="YichunZhang" modifier="YichunZhang" created="201310292041" modified="201312150250" changecount="7">
<pre>&lt;&lt;toolbar permalink&gt;&gt;
! Mainline Version 1.4.3.9 - 14 December 2013
* bugfix: the include path for LuaJIT C headers was still pointing to {{{luajit-2.0}}}, which should have been {{{luajit-2.1}}} instead. thanks Tor Hveem for the report.
! Mainline Version 1.4.3.7 - 14 December 2013
* upgraded LuaJIT to v2.1-20131211.
** see changes here: https://github.com/agentzh/luajit2/commits/v2.1-agentzh
* bundled LuaRestyCoreLibrary 0.0.2.
** this library reimplements LuaNginxModule's Lua API with LuaJIT FFI. see https://github.com/agentzh/lua-resty-core for more details.
* upgraded LuaNginxModule to 0.9.3.
** feature: added a lot of pure C API (without using any Lua VM's C API) for ~FFI-based Lua API implementations like LuaRestyCoreLibrary.
** feature: allow creating 0-delay timers upon worker process existing.
** feature: added new API function [[ngx.worker.exiting()|https://github.com/chaoslawful/lua-nginx-module#ngxworkerexiting]] for testing if the current worker process has started exiting.
** feature: [[ngx.re.find()|https://github.com/chaoslawful/lua-nginx-module#ngxrefind]] now accepts the optional 5th argument &quot;nth&quot; to control which submatch capture's indexes are returned. thanks Lance Li for the feature request.
** feature: added new API for version numbers of both Nginx and LuaNginxModule itself: [[ngx.config.nginx_version|https://github.com/chaoslawful/lua-nginx-module#ngxconfignginx_version]] and [[ngx.config.ngx_lua_version|https://github.com/chaoslawful/lua-nginx-module#ngxconfigngx_lua_version]]. thanks smallfish for the patch.
** feature: added support for loading LuaJIT 2.1 bytecode files directly in {{{*_by_lua_file}}} configuration directives.
** bugfix: [[ngx.req.set_header()|https://github.com/chaoslawful/lua-nginx-module#ngxreqset_header]] did not completely override the existing request header with multiple values. thanks Aviram Cohen for the report.
** bugfix: modifying request headers in a subrequest could lead to assertion failures and crashes. thanks leaf corcoran for the report.
** bugfix: turning off [[lua_code_cache|https://github.com/chaoslawful/lua-nginx-module#lua_code_cache]] could lead to memory issues (segfaults and LuaJIT assertion failures) when Lua libraries using LuaJIT FFI were used. now we always create a clean separate Lua VM instance for every Nginx request served by us when the Lua code cache is disabled. thanks Ron Gomes for the report.
** bugfix: the linker option {{{-E}}} is not support in Cygwin's linker; we should test {{{--export-all-symbols}}} at the same time. thanks Heero Zhang for the report.
** bugfix: fixed the warnings from the Microsoft Visual C++ compiler. thanks Edwin Cleton for the report.
** optimize: optimized the implementation of [[ngx.headers_sent|https://github.com/chaoslawful/lua-nginx-module#ngxheaders_sent]] a bit.
** doc: added new section &quot;Statically Linking Pure Lua Modules&quot;: https://github.com/chaoslawful/lua-nginx-module#statically-linking-pure-lua-modules
** doc: typo fixes from Zheng Ping.
* upgraded HeadersMoreNginxModule to 0.24.
** bugfix: [[more_set_input_headers|https://github.com/agentzh/headers-more-nginx-module#more_set_input_headers]] did not completely override the existing request header with multiple values. thanks Aviram Cohen for the report.
* upgraded SetMiscNginxModule to 0.23.
** feature: added new configuration directives [[set_formatted_gmt_time|https://github.com/agentzh/set-misc-nginx-module#set_formatted_gmt_time]] and [[set_formatted_local_time|https://github.com/agentzh/set-misc-nginx-module#set_formatted_local_time]]. thanks Trurl ~McByte for the patch.
* upgraded MemcNginxModule to 0.14.
** feature: added new configuration directive [[memc_ignore_client_abort|https://github.com/agentzh/memc-nginx-module#memc_ignore_client_abort]]. thanks Eldar Zaitov for the patch.
* upgraded RdsJsonNginxModule to 0.13.
** bugfix: fixed the warnings from the Microsoft Visual C++ compiler. thanks Edwin Cleton for the report.
* upgraded EchoNginxModule to 0.50.
** bugfix: fixed the warnings from the Microsoft Visual C++ compiler. thanks Edwin Cleton for the report.
* upgraded ArrayVarNginxModule to 0.03.
** bugfix: fixed the warnings from the Microsoft Visual C++ compiler. thanks Edwin Cleton for the report.
* upgraded RedisNginxModule module to 0.3.7.
** see changes here: http://mailman.nginx.org/pipermail/nginx/2013-December/041297.html
* feature: applied the [[larger_max_error_str patch|https://github.com/agentzh/ngx_openresty/blob/master/patches/nginx-1.4.3-larger_max_error_str.patch]] to the nginx core to allow error log messages up to 4096 bytes and to allow the C macro {{{NGX_MAX_ERROR_STR}}} to be overridden from the outside.
* feature: added new configure option {{{--with-pcre-conf-opt=OPTIONS}}} to the nginx core to allow custom PCRE ./configure build options. thanks Lance Li for the original patch.
! Stable Version 1.4.3.6 - 20 November 2013
* bugfix: applied the official patch [[patch.2013.space.txt|http://nginx.org/download/patch.2013.space.txt]] for the Nginx core to fix the security issue ~CVE-2013-4547.
! Stable Version 1.4.3.4 - 12 November 2013
This release is essentially the same as the last mainline release, 1.4.3.3.

The following components are bundled in this release:
* ~LuaJIT-2.0.2
* array-var-nginx-module-0.03rc1
* auth-request-nginx-module-0.2
* drizzle-nginx-module-0.1.6
* echo-nginx-module-0.49
* encrypted-session-nginx-module-0.03
* form-input-nginx-module-0.07
* headers-more-nginx-module-0.23
* iconv-nginx-module-0.10
* lua-5.1.5
* lua-cjson-1.0.3
* lua-rds-parser-0.05
* lua-redis-parser-0.10
* lua-resty-dns-0.10
* lua-resty-lock-0.01
* lua-resty-memcached-0.12
* lua-resty-mysql-0.14
* lua-resty-redis-0.17
* lua-resty-string-0.08
* lua-resty-upload-0.09
* lua-resty-websocket-0.02
* memc-nginx-module-0.13
* nginx-1.4.3
* ngx_coolkit-0.2rc1
* ngx_devel_kit-0.2.19
* ngx_lua-0.9.2
* ngx_postgres-1.0rc3
* rds-csv-nginx-module-0.05
* rds-json-nginx-module-0.12
* redis-nginx-module-0.3.6
* redis2-nginx-module-0.10
* set-misc-nginx-module-0.22
* srcache-nginx-module-0.24
* xss-nginx-module-0.04
! Mainline Version 1.4.3.3 - 6 November 2013
* upgraded LuaNginxModule to 0.9.2.
** feature: added new API function [[ngx.re.find()|https://github.com/chaoslawful/lua-nginx-module#ngxrefind]], which is similar to [[ngx.re.match|https://github.com/chaoslawful/lua-nginx-module#ngxrematch]], but only returns the beginning index and end index (1-based) of the whole match, which is 30% ~ 40% faster than {{{ngx.re.match}}} for simplest regexes.
** feature: added new API function [[ngx.config.prefix()|https://github.com/chaoslawful/lua-nginx-module#ngxconfigprefix]] to return the Nginx server &quot;prefix&quot; path.
** bugfix: reading [[ngx.header.HEADER|https://github.com/chaoslawful/lua-nginx-module#ngxheaderheader]] could result in Lua string storage corruptions. thanks Dane Knecht for the report.
** bugfix: [[ngx.re.match|https://github.com/chaoslawful/lua-nginx-module#ngxrematch]]: the &quot;ctx&quot; parameter table's &quot;pos&quot; field should start from 1 instead of 0.
** bugfix: fixed compilation errors with Nginx older than 1.0.0.
** bugfix: localizing the [[coroutine.*|https://github.com/chaoslawful/lua-nginx-module#coroutinecreate]] API functions in [[init_by_lua*|https://github.com/chaoslawful/lua-nginx-module#init_by_lua]] for future use in contexts like [[content_by_lua*|https://github.com/chaoslawful/lua-nginx-module#content_by_lua]] might hang the request. thanks James Hurst for the report.
* upgraded SrcacheNginxModule to 0.24.
** bugfix: fixed compilation errors with Nginx older than 0.9.2.
* bugfix: applied the [[cache_manager_exit patch|https://github.com/agentzh/ngx_openresty/blob/master/patches/nginx-1.4.3-cache_manager_exit.patch]] to the Nginx core to fix an issue when the cache manager process is shutting down.
! Mainline Version 1.4.3.1 - 29 October 2013
* upgraded the Nginx core to 1.4.3.
** see the changes here: http://nginx.org/en/CHANGES-1.4
* upgraded LuaNginxModule to 0.9.1.
** feature: added the new configuration directive [[lua_use_default_type|https://github.com/chaoslawful/lua-nginx-module#lua_use_default_type]] for controlling whether to send out a default {{{Content-Type}}} response header (as defined by the [[default_type|http://nginx.org/en/docs/http/ngx_http_core_module.html#default_type]] directive). default on. thanks aviramc for the patch.
** feature: now the raw request cosocket returned by [[ngx.req.socket(true)|https://github.com/chaoslawful/lua-nginx-module#ngxreqsocket]] no longer requires the request body to be read already, which means that one can use this cosocket to read the raw request body data as well. thanks aviramc for the original patch.
** bugfix: when there were no existing {{{Cache-Control}}} response headers, {{{ngx.header.cache_control = nil}}} would (incorrectly) create a new {{{Cache-Control}}} header with an empty value. thanks jinglong for the patch.
** bugfix: the original letter-case of the header name was lost when creating the {{{Cache-Control}}} response header via the [[ngx.header.HEADER|https://github.com/chaoslawful/lua-nginx-module#ngxheaderheader]] API.
** bugfix: [[ngx.req.set_header(&quot;Host&quot;, value)|https://github.com/chaoslawful/lua-nginx-module#ngxreqset_header]] would overwrite the value of [[$host|http://nginx.org/en/docs/http/ngx_http_core_module.html#var_host]] with bad values. thanks aviramc for the patch.
** bugfix: use of [[ngx.exit()|https://github.com/chaoslawful/lua-nginx-module#ngxexit]] to abort pending subrequests in other &quot;light threads&quot; might lead to segfault or request hang when HTTP 1.0 full buffering is in effect.
** bugfix: removing a request header might lead to memory corruptions. thanks Bjørnar Ness for the report.
** bugfix: reading [[ngx.status|https://github.com/chaoslawful/lua-nginx-module#ngxstatus]] might get different values than [[$status|http://nginx.org/en/docs/http/ngx_http_core_module.html#var_status]]. thanks Kevin Burke for the report.
** bugfix: downstream write events might interfere with upstream cosockets that are slow to write to. thanks Aviram Cohen for the report.
** bugfix: the bookkeeping state for already-freed user threads might be incorrectly used by newly-created threads that were completely different, which could lead to bad results. thanks Sam Lawrence for the report.
** bugfix: calling [[ngx.thread.wait()|https://github.com/chaoslawful/lua-nginx-module#ngxthreadwait]] on a user thread object that is already waited (i.e., already dead) would hang forever. thanks Sam Lawrence for the report.
** bugfix: the alert &quot;zero size buf&quot; could be logged when assigning an empty Lua string (&quot;&quot;) to {{{ngx.arg[1]}}} in [[body_filter_by_lua|https://github.com/chaoslawful/lua-nginx-module#body_filter_by_lua]]*.
** bugfix: subrequests initiated by [[ngx.location.capture|https://github.com/chaoslawful/lua-nginx-module#ngxlocationcapture]]* could trigger unnecessary response header sending actions in the subrequest because our capturing output header filter did not set {{{r-&gt;header_sent}}}.
** bugfix: the Lua error message for the case that [[ngx.sleep()|https://github.com/chaoslawful/lua-nginx-module#ngxsleep]] was used in [[log_by_lua|https://github.com/chaoslawful/lua-nginx-module#log_by_lua]]* was not friendly. thanks Jiale Zhi for the report.
** bugfix: now [[ngx.req.socket(true)|https://github.com/chaoslawful/lua-nginx-module#ngxreqsocket]] returns proper error when there is some other &quot;light thread&quot; reading the request body.
** bugfix: [[header_filter_by_lua|https://github.com/chaoslawful/lua-nginx-module#header_filter_by_lua]]*, [[body_filter_by_lua|https://github.com/chaoslawful/lua-nginx-module#body_filter_by_lua]]*, and [[ngx.location.capture|https://github.com/chaoslawful/lua-nginx-module#ngxlocationcapture]]* might not work properly with multiple &quot;http {}&quot; blocks in {{{nginx.conf}}}. thanks flygoast for the report.
** optimize: made [[ngx.re.match|https://github.com/chaoslawful/lua-nginx-module#ngxrematch]] and [[ngx.re.gmatch|https://github.com/chaoslawful/lua-nginx-module#ngxregmatch]] faster for LuaJIT 2.x when there is no submatch captures.
** optimize: pre-allocate space for the Lua tables in various places.
** doc: fixed the context for the [[lua_package_path|https://github.com/chaoslawful/lua-nginx-module#lua_package_path]] and [[lua_package_cpath|https://github.com/chaoslawful/lua-nginx-module#lua_package_cpath]] directives. thanks duhoobo for the report.
* upgraded HeadersMoreNginxModule to 0.23.
** bugfix: removing request headers via [[more_clear_input_headers|https://github.com/agentzh/headers-more-nginx-module#more_clear_input_headers]] might lead to memory corruptions.
** bugfix: [[more_set_input_headers|https://github.com/agentzh/headers-more-nginx-module#more_set_input_headers]] might overwrite the value of the [[$host|http://nginx.org/en/docs/http/ngx_http_core_module.html#var_host]] variable with bad values.
** bugfix: [[more_set_headers|https://github.com/agentzh/headers-more-nginx-module#more_set_headers]] and [[more_clear_headers|https://github.com/agentzh/headers-more-nginx-module#more_clear_headers]] might not work when multiple &quot;http {}&quot; blocks were used in {{{nginx.conf}}}.
** bugfix: eliminated use of C global variables during configuration phase because it might lead to issues when HUP reload failed.
* upgraded SrcacheNginxModule to 0.23.
** bugfix: this module might not work properly with multiple &quot;http {}&quot; blocks in {{{nginx.conf}}}.
** bugfix: we might (incorrectly) return 500 in our output filters.
** bugfix: we did not set {{{r-&gt;header_sent}}} when we want to discard the header in our header filter.
* upgraded RdsJsonNginxModule to 0.12.
** bugfix: in case of multiple &quot;http {}&quot; blocks in {{{nginx.conf}}}, our output filters might be disabled even when this module is configured properly.
** bugix: we did not check the {{{NULL}}} pointer returned by an Nginx array element allocation.
* upgraded RdsCsvNginxModule to 0.05.
** optimize: we now only register our output filters when this module is indeed used (the only exception is when multiple &quot;http {}&quot; blocks are used).
* upgraded XssNginxModule to 0.04.
** optimize: we now only register our output filters when this module is indeed used (the only exception is when multiple &quot;http {}&quot; blocks are used).
* upgraded EchoNginxModule to 0.49.
** bugfix: [[echo_before_body|https://github.com/agentzh/echo-nginx-module#echo_before_body]] and [[echo_after_body|https://github.com/agentzh/echo-nginx-module#echo_after_body]] might now work properly when multiple &quot;http {}&quot; blocks were used in {{{nginx.conf}}}.
* upgraded LuaRestyRedisLibrary to 0.17.
** optimize: added an optional argument &quot;n&quot; to [[init_pipeline()|https://github.com/agentzh/lua-resty-redis#init_pipeline]] as a hint for the number of pipelined commands.
** optimize: use LuaJIT 2.1's new [[table.new() primitive|http://repo.or.cz/w/luajit-2.0.git/commit/c8cfca055]] to pre-allocate space for Lua tables.
* upgraded LuaRestyUploadLibrary to 0.09.
** bugfix: removed use of the [[module()|http://www.lua.org/manual/5.1/manual.html#pdf-module]] function to prevent bad side-effects.
** optimize: Removed use of lua tables and [[table.concat()|http://www.lua.org/manual/5.1/manual.html#pdf-table.concat]] for simple one-line Lua string concatenations.
* upgraded LuaRestyMySQLLibrary to 0.14.
** bugfix: avoided using Lua 5.1's [[module()|http://www.lua.org/manual/5.1/manual.html#pdf-module]] function for defining our Lua modules because it has bad side effects.
** optimize: added an optional new argument &quot;nrows&quot; to the [[query()|https://github.com/agentzh/lua-resty-mysql#query]] and [[read_result()|https://github.com/agentzh/lua-resty-mysql#read_result]] methods, which can speed up things a bit.
** optimize: use LuaJIT v2.1's new [[table.new()|http://repo.or.cz/w/luajit-2.0.git/commit/c8cfca055]] API to optimize Lua table allocations. when table.new is missing, just fall back to the good old &quot;{}&quot; constructor. this gives 12% overall speed-up for a typical result set with 500 rows when LuaJIT 2.1 is used.
** optimize: eliminated use of [[table.insert()|http://www.lua.org/manual/5.1/manual.html#pdf-table.insert]] because it is slower than &quot;tb[#tb + 1] = val&quot;.
** optimize: switched over to the multi-argument form of [[string.char()|http://www.lua.org/manual/5.1/manual.html#pdf-string.char]].
** optimize: no longer use Lua tables and [[table.concat()|http://www.lua.org/manual/5.1/manual.html#pdf-table.concat]] to construct simple query strings.
* upgraded LuaRestyWebSocketLibrary to 0.02.
** optimize: use LuaJIT 2.1's [[table.new()|http://repo.or.cz/w/luajit-2.0.git/commit/c8cfca055]] to preallocate space for Lua tables, eliminating the overhead of Lua table rehash.
* feature: applied the [[proxy_host_port_vars patch|https://github.com/agentzh/ngx_openresty/blob/master/patches/nginx-1.4.3-proxy_host_port_vars.patch]] to the Nginx core to make {{{$proxy_host}}} and {{{$proxy_port}}} accessible for dynamic languages like Lua and Perl.
* bugfix: applied the [[gzip_flush_bug patch|https://github.com/agentzh/ngx_openresty/blob/master/patches/nginx-1.4.3-gzip_flush_bug.patch]] to the Nginx core to fix request hang caused by the [[ngx_gzip|http://nginx.org/en/docs/http/ngx_http_gzip_module.html]] and [[ngx_gunzip|http://nginx.org/en/docs/http/ngx_http_gzip_module.html]] modules when using [[ngx.flush(true)|https://github.com/chaoslawful/lua-nginx-module#ngxflush]], for example. Thanks Maxim Dounin for the review.
* bugfix: applied the [[cache_lock_hang_in_subreq patch|https://github.com/agentzh/ngx_openresty/blob/master/patches/nginx-1.4.3-cache_lock_hang_in_subreq.patch]] to the Nginx core to fix the request hang when using [[proxy_cache_lock|http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_cache_lock]] in subrequests and the cache lock timeout happens.
* bugfix: backported Maxim Dounin's [[patch|https://github.com/agentzh/ngx_openresty/blob/master/patches/nginx-1.4.3-gzip_buffered_bug.patch]] to fix an issue in the [[ngx_gzip module|http://nginx.org/en/docs/http/ngx_http_gzip_module.html]]: it did not clear {{{r-&gt;connection-&gt;buffered}}} when the pending data was already flushed out. this could hang LuaNginxModule's [[ngx.flush(true)|https://github.com/chaoslawful/lua-nginx-module#ngxflush]] call, for example.
See ChangeLog1004002 for change log for OpenResty 1.4.2.x.</pre>
</div>
<div title="ChangeLog1005008" creator="YichunZhang" modifier="YichunZhang" created="201401110427" changecount="1">
<pre>&lt;&lt;toolbar permalink&gt;&gt;
! Mainline Version 1.5.8.1 - 10 January 2014
* change: now we default to LuaJIT instead of the standard Lua 5.1 interpreter. the {{{--with-luajit}}} option for {{{./configure}}} is now the default. To use the standard Lua 5.1 interpreter, specify the {{{--with-lua51}}} option explicitly.
* bugfix: Nginx's built-in resolver did not accept fully qualified domain names (with a trailing dot).
* optimize: shortened the {{{Server}}} response header string &quot;ngx_openresty&quot; to &quot;openresty&quot;.
* upgraded the Nginx core to 1.5.8.
** see the changes here: http://nginx.org/en/CHANGES
* upgraded LuaJIT to v2.1-20140109.
** bugfix: fixed ABC (Array Bounds Check) elimination. (Mike Pall)
** bugfix: fixed ~MinGW build. (Mike Pall)
** bugfix: x86: fixed stack slot counting for ~IR_CALLA (affects table.new). (Mike Pall) this could lead to random table field missing issues in LuaRestyMySQLLibrary on i386. thanks lhmwzy for the report.
** bugfix: fixed compilation of {{{string.byte(s, nil, n)}}}. (Mike Pall)
** bugfix: MIPS: Cosmetic fix for interpreter. (Mike Pall)
* upgraded LuaNginxModule to 0.9.4.
** feature: allow use of [[ngx.exit()|https://github.com/chaoslawful/lua-nginx-module#ngxexit]] in the context of [[header_filter_by_lua*|https://github.com/chaoslawful/lua-nginx-module#header_filter_by_lua]] to perform a &quot;filter finalization&quot;. but in this context [[ngx.exit()|https://github.com/chaoslawful/lua-nginx-module#ngxexit]] is an asynchronous operation and returns immediately.
** feature: added the optional 5th argument, &quot;res_table&quot;, to [[ngx.re.match()|https://github.com/chaoslawful/lua-nginx-module#ngxrematch]] which is the user-supplied result table for the resulting captures. This feature can give 12%+ speedup for simple [[ngx.re.match()|https://github.com/chaoslawful/lua-nginx-module#ngxrematch]] calls with 4 submatch captures.
** feature: [[ngx.escape_uri()|https://github.com/chaoslawful/lua-nginx-module#ngxescape_uri]] and [[ngx.unescape_uri()|https://github.com/chaoslawful/lua-nginx-module#ngxunescape_uri]] now accept a {{{nil}}} argument, which is equivalent to an empty string.
** feature: added new pure C API, {{{ngx_http_lua_ffi_max_regex_cache_size}}}, for ~FFI-based implementations like LuaRestyCoreLibrary.
** change: [[ngx.decode_base64()|https://github.com/chaoslawful/lua-nginx-module#ngxdecode_base64]] now only accepts string arguments.
** bugfix: coroutines might incorrectly enter the &quot;dead&quot; state even right after creation with [[coroutine.create()|https://github.com/chaoslawful/lua-nginx-module#coroutinecreate]]. thanks James Hurst for the report.
** bugfix: segmentation fault might happen when aborting a &quot;light thread&quot; pending on downstream cosocket writes. thanks Aviram Cohen for the report.
** bugfix: we might try sending the response header again in [[ngx.exit()|https://github.com/chaoslawful/lua-nginx-module#ngxexit]] when the header was already sent.
** bugfix: subrequests initiated by [[ngx.location.capture()|https://github.com/chaoslawful/lua-nginx-module#ngxlocationcapture]] might send their own response headers more than once. this issue might also lead to the alert message &quot;header already sent&quot; and request aborts when nginx 1.5.4+ was used.
** bugfix: fixed incompatibilities in Nginx 1.5.8 which breaks the resolver API in the Nginx core.
** bugfix: fixed a compilation warning when PCRE is disabled in the build. thanks Jay for the patch.
** bugfix: we did not set the shortcut fields in {{{r-&gt;headers_in}}} for request headers in our subrequests created by [[ngx.location.capture*()|https://github.com/chaoslawful/lua-nginx-module#ngxlocationcapture]], which might cause inter-operative issues with other Nginx modules. thanks Aviram Cohen for the original patch.
** optimize: we no longer clear the {{{lua_State}}} pointers for dead &quot;light threads&quot; such that their coroutine context structs could be reused by other &quot;light threads&quot; and user coroutines. this can lead to smaller memory footprint.
** doc: documented that the [[coroutine.*|https://github.com/chaoslawful/lua-nginx-module#coroutinecreate]] API can be used in [[init_by_lua|https://github.com/chaoslawful/lua-nginx-module#init_by_lua]]* since 0.9.2. thanks Ruoshan Huang for the reminder.
* upgraded LuaRestyMemcachedLibrary to 0.13.
** optimize: saved one cosocket [[receive()|https://github.com/chaoslawful/lua-nginx-module#tcpsockreceive]] call in the [[get()|https://github.com/agentzh/lua-resty-memcached#get]] and [[gets()|https://github.com/agentzh/lua-resty-memcached#gets]] methods.
** bugfix: the Memcached connection might enter a bad state when read timeout happens because LuaNginxModule's cosocket reading calls no longer automatically close the connection in this case. thanks Dane Knecht for the report.
* upgraded LuaRestyRedisLibrary to 0.18.
** optimize: eliminated one (potentially expensive) {{{string.sub()}}} call in the Redis reply parser.
** bugfix: the Redis connection might enter a bad state when read timeout happens because LuaNginxModule's cosocket reading calls no longer automatically close the connection in this case.
* upgraded LuaRestyLockLibrary to 0.02.
** bugfix: the [[lock()|https://github.com/agentzh/lua-resty-lock#lock]] method accepted nil keys silently.
* upgraded LuaRestyDNSLibrary to 0.11.
** bugfix: avoided use of the module() built-in to define the Lua module.
** bugfix: we did not reject bad domain names with a leading dot. thanks Dane Knecht for the report.
** bugfix: error handling fixes in the [[query|https://github.com/agentzh/lua-resty-dns#query]] and [[tcp_query|https://github.com/agentzh/lua-resty-dns#tcp_query]] methods.
* upgraded LuaRestyCoreLibrary to 0.0.3.
** feature: updated to comply with LuaNginxModule 0.9.4.
** bugfix: [[resty.core.regex|https://github.com/agentzh/lua-resty-core#restycoreregex]]: the [[ngx.re|https://github.com/chaoslawful/lua-nginx-module#ngxrematch]] API did not honour the [[lua_regex_cache_max_entries|https://github.com/chaoslawful/lua-nginx-module#lua_regex_cache_max_entries]] configuration directive.
** optimize: [[ngx.re.gsub|https://github.com/chaoslawful/lua-nginx-module#ngxregsub]] used to use literal type string &quot;const char *&quot; in ffi.cast() which is expensive in interpreter mode. now we use the ctype object directly, which leads to 11% in interpreter mode.
* upgraded EchoNginxModule to 0.51.
** bugfix: for Nginx 1.2.6+ and 1.3.9+, the main request reference count might go out of sync when Nginx's request body reader returned status code 300+. thanks Hungpu DU for the report.
** bugfix: [[echo_request_body|https://github.com/agentzh/echo-nginx-module#echo_request_body]] truncated the response body prematurely when the request body was in memory (because the request reader sets &quot;last_buf&quot; in this case). thanks Hungpu DU for the original patch.
** bugfix: using [[$echo_timer_elapsed|https://github.com/agentzh/echo-nginx-module#echo_timer_elapsed]] variable alone in the configuration caused segmentation faults. thanks Hungpu DU for the report.
** doc: typo fix in the [[echo_foreach_split|https://github.com/agentzh/echo-nginx-module#echo_foreach_split]] sample code. thanks Hungpu DU for the report.
* upgraded DrizzleNginxModule to 0.1.7.
** bugfix: fixed most of warnings and errors from the Microsoft Visual C++ compiler, reported by Edwin Cleton.
* upgraded HeadersMoreNginxModule to 0.25.
** bugfix: fixed a warning from the Microsoft C compiler. thanks Edwin Cleton for the report.
** doc: documented the limitation that we cannot remove the &quot;Connection&quot; response header with this module. thanks Michael Orlando for bringing this up.
* upgraded SetMiscNginxModule to 0.24.
** bugfix: fixed the warnings from the Microsoft C compiler. thanks Edwin Cleton for the report.
* upgraded SrcacheNginxModule to 0.25.
** feature: now the value specified in [[srcache_store_skip|https://github.com/agentzh/srcache-nginx-module#srcache_store_skip]] is evaluated and tested again right after the end of the response body data stream is seen. thanks Eldar Zaitov for the patch.
See ChangeLog1004003 for change log for OpenResty 1.4.3.x.</pre>
</div>
<div title="ChangeLog1005011" creator="YichunZhang" modifier="YichunZhang" created="201403310512" changecount="1">
<pre>&lt;&lt;toolbar permalink&gt;&gt;
! Version 1.5.11.1 - 30 March 2014
* upgraded LuaJIT to v2.1-20140330.
** feature: included Mike Pall's new &quot;trace stitching&quot; feature that can compile around most of the [[NYI items|http://wiki.luajit.org/NYI]]. thanks [[CloudFlare Inc.|http://www.cloudflare.com/]] for sponsoring the development. This helps compiling more Lua code. For example, it gives 10% ~ 40% speedup in simple test cases of LuaRestyMySQLLibrary out of the box.
** bugfix: included all the new bug fixes from Mike Pall, most of which are very obscure bugs in the JIT compiler hidden for years.
** relaxed the hard-coded heuristic limit further to 100 for loopunroll.
** feature: applied John Marino's patch for compiling LuaJIT on ~DragonFlyBSD. thanks lhmwzy for proposing the patch.
* upgraded the Nginx core to 1.5.11.
** see the changes here: http://nginx.org/en/CHANGES
* bugfix: applied the patch to the NGINX core for the latest SPDY security vulnerability (~CVE-2014-0133).
* feature: added support for ~DragonFlyBSD to {{{./configure}}}. thanks lhmwzy for the patch.
* bugfix: disabled the -Werror option for clang because it caused build failures at least in recent Mac OS X systems. thanks Hamish Forbes for the report.
* feature: bundled new component LuaRestyUpstreamHealthcheckLibrary 0.01.
** see the documentation for details: https://github.com/agentzh/lua-resty-upstream-healthcheck#readme
* feature: bundled new component LuaUpstreamNginxModule 0.01.
** see the documentation for details: https://github.com/agentzh/lua-upstream-nginx-module#readme
* upgraded LuaNginxModule to 0.9.6.
** feature: added new configuration directives, [[init_worker_by_lua|https://github.com/chaoslawful/lua-nginx-module/#init_worker_by_lua]] and [[init_worker_by_lua_file|https://github.com/chaoslawful/lua-nginx-module/#init_worker_by_lua_file]], to run Lua code upon every nginx worker process's startup.
** feature: added new API function [[ngx.config.nginx_configure()|https://github.com/chaoslawful/lua-nginx-module/#ngxconfignginx_configure]] to return the NGINX {{{./configure}}} arguments string to the Lua land. thanks Tatsuhiko Kubo for the patch.
** feature: added new API function [[ngx.resp.get_headers()|https://github.com/chaoslawful/lua-nginx-module/#ngxrespget_headers]] for fetching all the response headers. thanks Tatsuhiko Kubo for the patch.
** feature: added new API function [[ngx.worker.pid()|https://github.com/chaoslawful/lua-nginx-module/#ngxworkerpid]] for retrieving the current nginx worker process's pid.
** feature: explicitly check Lua langauge version mismatch; we only accept the Lua 5.1 language (for now).
** bugfix: accessing a cosocket object from a request which does not create it could lead to segmentation faults. now we throw out a Lua error &quot;bad request&quot; properly in this case.
** change: it is now the user's responsibility to clear the captures table for [[ngx.re.match()|https://github.com/chaoslawful/lua-nginx-module/#ngxrematch]].
** bugfix: we should prefix our chunk names for from-string lua source (which also leads to nicer error messages). thanks Mike Pall for the catch.
** bugfix: subrequests initiated by [[ngx.location.capture*|https://github.com/chaoslawful/lua-nginx-module/#ngxlocationcapture]] with the HEAD method did not result in responses without response bodies. thanks Daniel for the report.
**  bugfix: segfault might happen in the FFI API for destroying compiled PCRE regexes, which affects libraries like LuaRestyCoreLibrary. thanks Dane Kneche.
** bugfix: fixes for small string buffer arguments in the C API for ~FFI-based implementations of [[shdict:get()|https://github.com/chaoslawful/lua-nginx-module/#ngxshareddictget]].
** bugfix: fixed the error message buffer overwrite in the C API for ~FFI-based [[ngx.re|https://github.com/chaoslawful/lua-nginx-module/#ngxrematch]] implementations.
** bugfix: use of the public C API in other nginx C modules (extending LuaNginxModule) lead to compilation errors and warnings when the Microsoft C compiler is used. thanks Edwin Cleton for the report.
** bugfix: segmentation faults might happen when multiple &quot;light threads&quot; in the same request manipuate a stream cosocket object in turn. thanks Aviram Cohen for the report.
** bugfix: timers created by ngx.timer.at() might not be aborted prematurely upon nginx worker exit. thanks Hamish Forbes for the report.
** bugfix: the return value sizes of the C functions {{{ngx_http_lua_init_by_inline}}} and {{{ngx_http_lua_init_by_file}}} were wrong.
** optimize: coroutine status string look-up is now a bit more efficient by specifying the string lengths explicitly. thanks Tatsuhiko Kubo for the patch.
** various code refactoring.
* upgraded LuaRestyCoreLibrary to 0.0.5.
** change: now it is the user's responsibility to clear the input result table.
** feature: [[resty.core.regex|https://github.com/agentzh/lua-resty-core#restycoreregex]]: added new function {{{set_buf_grow_ratio}}} to control the buffer grow ratio (default 2.0).
** bugfix: segmentation fault might happen due to assignments to [[ngx.header.HEADER|https://github.com/chaoslawful/lua-nginx-module/#ngxheaderheader]] because we did not anchor the memory buffer properly which might get collected prematurely.
** bugfix: [[ngx.req.get_headers|https://github.com/chaoslawful/lua-nginx-module/#ngxreqget_headers]]: we need to anchor the string buffer being casted otherwise it might be accidentally garbage collected when we still hold a C pointer to it. this bug might lead to segmentation faults.
** optimize: cache the match captures table for [[ngx.re.gsub()|https://github.com/chaoslawful/lua-nginx-module/#ngxregsub]] when a function-typed &quot;replace&quot; argument is specified. this gives a remarkable speedup.
** optimize: [[resty.core.regex|https://github.com/agentzh/lua-resty-core#restycoreregex]]: forked the original shared code paths to multiple specialized versions, which helps the JIT compiler.
** optimize: [[resty.core.regex|https://github.com/agentzh/lua-resty-core#restycoreregex]]: cache the parsing results for the regex option strings. thanks Mike Pall for the suggestion.
* upgraded LuaRestyRedisLibrary to 0.20.
** feature: added new redis 2.8.0 commands: {{{scan}}}, {{{sscan}}}, {{{hscan}}}, and {{{zscan}}}. thanks Dragonoid for the patch.
** feature: [[the read_reply()|https://github.com/agentzh/lua-resty-redis#read_reply]] method can now be re-tried immediately after a &quot;timeout&quot; error is returned.
** bugfix: the {{{unsubscribe}}}/{{{subscribe}}} commands could not be called after [[read_reply()|https://github.com/agentzh/lua-resty-redis#read_reply]] returned &quot;timeout&quot;. thanks doujiang for the patch.
** bugfix: we incorrectly allowed reusing redis connections in the &quot;subscribed&quot; state. thanks doujiang for the patch.
* upgraded LuaCjsonLibrary to 2.1.0.1.
** rebased on lua-cjson 2.1.0: http://www.kyne.com.au/~mark/software/NEWS-lua-cjson.txt the most notable new feature is the {{{cjson.safe}}} module.
** feature: applied Jiale Zhi's patch to add the new config function {{{encode_empty_table_as_object}}} so that we can encode empty Lua tables into empty JSON arrays.
* upgraded SrcacheNginxModule to 0.26.
** bugfix: HEAD requests might result in response bodies.
* upgraded EchoNginxModule to 0.52.
** bugfix: HEAD subrequests could still result in non-empty response bodies.
See ChangeLog1005008 for change log for OpenResty 1.5.8.x.</pre>
</div>
<div title="ChangeLog1005012" creator="YichunZhang" modifier="YichunZhang" created="201404292122" modified="201404302354" changecount="2">
<pre>&lt;&lt;toolbar permalink&gt;&gt;
! Version 1.5.12.1 - 29 April 2014
* upgraded the Nginx core to 1.5.12.
** see the changes here: http://nginx.org/en/CHANGES
* upgraded LuaJIT to v2.1-20140423 (see https://github.com/openresty/luajit2/releases ).
** bugfix: prevent adding side traces for stack checks. (Mike pall) this could cause internal assertion failure in the JIT compiler while replaying snapshots in very obscure cases: {{{lj_snap.c:497: lj_snap_replay: Assertion `ir-&gt;o == IR_CONV &amp;&amp; ir-&gt;op2 == ((IRT_NUM&lt;&lt;5)|IRT_INT)' failed.}}}
** bugfix: fixed FOLD of string concatenations. (Mike Pall) this issue was reported by leafo and could lead to invalid string results in special cases while compiling string concatenations.
** bugfix: FFI: fixed cdata equality comparison against strings and other Lua types. (Mike Pall)
** bugfix: fixed top slot calculation for snapshots with continuations. (Mike Pall) this was a bug in snapshot generation, but it only surfaced with trace stitching. it could cause Lua stack overwrites in special cases.
** bugfix: PPC: don't use mcrxr on PPE. (Mike Pall)
** bugfix: prevent GC estimate miscalculation due to buffer growth. (Mike Pall)
** bugfix: fixed the regression introduced by the previous fix for &quot;reuse of SCEV results in FORL&quot;. (Mike Pall) this could cause internal assertion failure in the JIT compiler: {{{lj_record.c:68: rec_check_ir: Assertion `op2 &gt;= nk' failed.}}}
** bugfix: fixed alias analysis for {{{table.len}}} vs. {{{table.clear}}}. (Mike Pall) this could cause {{{table.len}}} to return incorrect values (nonzero values) after {{{table.clear}}} was performed.
** bugfix: fixed the compatibility with ~DragonFlyBSD. thanks lhmwzy for the patch.
** feature: allow non-scalar cdata to be compared for equality by address. (Mike Pall)
* upgraded LuaUpstreamNginxModule to 0.02.
** bugfix: upstream names did not support taking a port number. thanks magicleo for the report.
* upgraded Redis2NginxModule to 0.11.
** change: now we always ignore client aborts for collaborations with other modules like SrcacheNginxModule. thanks akamatgi for the report.
* upgraded LuaNginxModule to 0.9.7.
**  bugfix: when [[lua_code_cache|https://github.com/openresty/lua-nginx-module#lua_code_cache]] was off, [[cosocket:setkeepalive()|https://github.com/openresty/lua-nginx-module#tcpsocksetkeepalive]] might lead to segmentation faults. thanks Kelvin Peng for the report.
** refactor: improved the error handling and logging in the Lua code loader and closure factory.
** change: added stronger assertions to the stream-typed cosocket implementation.
** optimize: we no longer call {{{ngx_pfree()}}} in our own {{{pcre_free}}} hook.
** optimize: we no longer clear the pointer {{{ctx-&gt;user_co_ctx}}} in {{{ngx_http_lua_reset_ctx}}}.
* upgraded EchoNginxModule to 0.53.
** bugfix: use of empty arguments after the {{{-n}}} option of the [[echo|https://github.com/openresty/echo-nginx-module#echo]] directive (and its friends) might cause subsequent arguments to get discarded. thanks Lice Pan for the report and fix.
* upgraded FormInputNginxModule to 0.08.
** bugfix: segmentation fault might happen when {{{set_form_input_multi}}} was used while no proper {{{Content-Type}}} request header was given.
* upgraded LuaRestyWebSocketLibrary to 0.03.
** optimize: added a minor optimization in the [[recv_frame()|https://github.com/openresty/lua-resty-websocket#recv_frame]] method. thanks yurnerola for the catch.
* upgraded LuaRestyCoreLibrary to 0.0.6.
** optimize: [[ngx.re.sub|https://github.com/openresty/lua-nginx-module#ngxresub]]/[[ngx.re.gsub|https://github.com/openresty/lua-nginx-module#ngxregsub]]: now we avoid constructing new Lua strings for the regex cache keys, which gives 5% speedup for trivial use cases.
** optimize: [[ngx.re.match|https://github.com/openresty/lua-nginx-module#ngxrematch]]/[[ngx.re.find|https://github.com/openresty/lua-nginx-module#ngxrefind]]: avoided constructing a new Lua string for the regex cache key by switching over to a cascaded 2-level hash table, which gives 22% speedup for simple use cases.
* upgraded LuaRestyLockLibrary to 0.03.
** bugfix: prevented using cdata directly as table keys.
* upgraded LuaRestyStringLibrary to 0.09.
** bugfix: avoided using the &quot;module&quot; builtin function to define lua modules. thanks lhmwzy for the original patch.
See ChangeLog1005011 for change log for OpenResty 1.5.11.x.</pre>
</div>
<div title="ChangeLog1007000" creator="YichunZhang" modifier="YichunZhang" created="201406072252" changecount="1">
<pre>&lt;&lt;toolbar permalink&gt;&gt;
! Version 1.7.0.1 - 7 June 2014
* upgraded the Nginx core to 1.7.0.
** see the changes here: http://nginx.org/en/CHANGES
* feature: bundled new Lua library, LuaRestyLrucacheLibrary, which is also enabled by default. see https://github.com/openresty/lua-resty-lrucache#readme for more details. thanks Shuxin Yang for the help.
* upgraded LuaJIT to v2.1-20140531: https://github.com/openresty/luajit2/tags
** imported Mike Pall's latest bug fixes and other changes:
*** Fix frame traversal while searching for error function.
*** Fix FOLD rule for STRREF of SNEW.
*** FFI: Fix recording of indexing a struct pointer ctype object itself.
*** FFI: Another fix for cdata equality comparisons.
*** Fix FOLD rule for {{{string.sub(s, ...) == k}}}.
*** x86: Fix code generation for unused result of {{{math.random()}}}.
*** x64: Workaround for MSVC build issue.
*** PPC: Fix red zone overflow in machine code generation.
*** Fix compatibility issues with Illumos. Thanks to Theo Schlossnagle.
*** Add PS Vita port. Thanks to Anton Stenmark.
** disabled trace stitching by default for now since it may trigger random lua stack corruptions when using with ngx_lua.
** feature: jit.dump: output Lua source location after every BC.
** feature: added internal memory-buffer-based trace entry/exit/start-recording event logging, mainly for debugging bugs in the JIT compiler. it requires {{{-DLUA_USE_TRACE_LOGS}}} when building.
** feature: save {{{g-&gt;jit_base}}} to {{{g-&gt;saved_jit_base}}} before {{{lj_err_throw}}} clears {{{g-&gt;jit_base}}} which makes it impossible to get Lua backtrace in such states.
* upgraded LuaRestyCoreLibrary to 0.0.7.
** feature: implemented [[ngx.req.set_header()|https://github.com/openresty/lua-nginx-module/#ngxreqset_header]] (partial: table-typed values not yet supported) and [[ngx.req.clear_header()|https://github.com/openresty/lua-nginx-module/#ngxreqclear_header]] with FFI in the [[resty.core.request|https://github.com/openresty/lua-resty-core#restycorerequest]] module.
** feature: implemented [[shdict:flush_all()|https://github.com/openresty/lua-nginx-module/#ngxshareddictflush_all]] with FFI in the [[resty.core.shdict|https://github.com/openresty/lua-resty-core#restycoreshdict]].
** feature: implemented [[ngx.req.set_method()|https://github.com/openresty/lua-nginx-module/#ngxreqset_method]] with FFI in [[resty.core.request|https://github.com/openresty/lua-resty-core#restycorerequest]].
** feature: implemented [[ngx.req.get_method()|https://github.com/openresty/lua-nginx-module/#ngxreqget_method]] with FFI in [[resty.core.request|https://github.com/openresty/lua-resty-core#restycorerequest]].
** feature: implemented [[ngx.time()|https://github.com/openresty/lua-nginx-module/#ngxtime]] with FFI in [[resty.core.time|https://github.com/openresty/lua-resty-core#restycoretime]].
** feature: implemented [[ngx.req.start_time|https://github.com/openresty/lua-nginx-module/#ngxreqstart_time]] with FFI in [[rest.core.request|https://github.com/openresty/lua-resty-core#restycorerequest]].
** feature: implemented [[ngx.now()|https://github.com/openresty/lua-nginx-module/#ngxnow]] with FFI in [[resty.core.time|https://github.com/openresty/lua-resty-core#restycoretime]].
* upgraded LuaNginxModule to 0.9.8.
** bugfix: the [[ngx.ctx|https://github.com/openresty/lua-nginx-module/#ngxctx]] table might be released prematurely when [[ngx.exit()|https://github.com/openresty/lua-nginx-module/#ngxexit]] was used to generate the response header. thanks Monkey Zhang for the report. now we always release [[ngx.ctx|https://github.com/openresty/lua-nginx-module/#ngxctx]] in our request pool cleanup handler.
** bugfix: we did not call our coroutine cleanup handlers right after our coroutine completes (either successfully or unsuccessfully) otherwise segmentation fault might happen when the Lua VM throws out unexpected exceptions like &quot;attempt to yield across C-call boundary&quot;. thanks Lipin Dmitriy for the report.
** bugfix: nginx does not guarentee the parent pointer of the rbtree root is meaningful, which could lead to inifinite loops when LuaNginxModule tried to abort pending timers prematurely (upon worker exit). thanks pengqi for the report.
** bugfix: [[ngx.req.set_method()|https://github.com/openresty/lua-nginx-module/#ngxreqset_method]]: we incorrectly modified {{{r-&gt;method}}} when the method ID was wrong.
** bugfix: [[rewrite_by_lua*|https://github.com/openresty/lua-nginx-module/#rewrite_by_lua]] and [[access_by_lua*|https://github.com/openresty/lua-nginx-module/#access_by_lua]] will now terminate the current request if the response header has already been sent (via calls like [[ngx.say|https://github.com/openresty/lua-nginx-module/#ngxsay]] and [[ngx.send_headers|https://github.com/openresty/lua-nginx-module/#ngxsend_headers]]) at that point. thanks yaronli and Sophos for the report.
** bugfix: issues in the error handling for pure C API functions for shared dict. thanks Xiaochen Wang.
** feature: now we save the original pattern string pointer value into our {{{ngx_http_lua_regex_t}}} C struct, to help runtime regex profiling and debugging.
** feature: allow use of 3rd-party pcre bindings in [[init_by_lua*|https://github.com/openresty/lua-nginx-module/#init_by_lua]]. thanks ikokostya for the feature request.
** feature: added pure C API functions to support the new ~FFI-based Lua API implemented in LuaRestyCoreLibrary.
** feature: make use of the new shm API in nginx 1.5.13+ to suppress the &quot;no memory&quot; error logging when the shared dictionaries run out of memory.
** feature: added C macro {{{NGX_LUA_ABORT_AT_PANIC}}} to allow generating a core dump when the Lua VM panics.
* upgraded SrcacheNginxModule to 0.27.
** bugfix: we used to skip all the output header and body filters run before our filters (which unfortunately bypassed the standard ngx_http_not_modified_filter_module, for example). thanks Lloyd Zhou for the report.
** feature: added new config directive [[srcache_store_ranges|https://github.com/openresty/srcache-nginx-module#srcache_store_ranges]] for storing 206 Partial Content responses generated by the standard ngx_http_range_filter_module.
* bugfix: updated the dtrace patch because systemtap 2.5 no longer accepts the {{{-xnolib}}} option in its dtrace utility.
* removed our bundled version of {{{ngx_http_auth_request_module}}} because recent versions of the nginx core already have it. thanks ~LazyZhu for the report.
* bugfix: applied our patch for the nginx core to fix the long standing memory fragmentation issue for blocks larger than the page size in the nginx slab allocator: http://mailman.nginx.org/pipermail/nginx-devel/2014-May/005316.html thanks Shuxin Yang for the help.
See ChangeLog1005012 for change log for OpenResty 1.5.12.x.</pre>
</div>
<div title="ChangeLog1007002" creator="YichunZhang" modifier="YichunZhang" created="201407130321" changecount="1">
<pre>&lt;&lt;toolbar permalink&gt;&gt;
! Version 1.7.2.1 - 12 July 2014
* upgraded the Nginx core to 1.7.2.
** see the changes here: http://nginx.org/en/CHANGES
* upgraded LuaJIT to v2.1-20140707: https://github.com/openresty/luajit2/tags
** imported Mike Pall's latest bug fixes and other changes:
*** feature: compile debug.getmetatable(). Thanks to Karel Tuma.
*** bugfix: Fix ABC elimination (for negative table indexes, for example).
*** bugfix: FFI: Fix compilation of reference field access.
*** bugfix: FFI: fixed frame traversal for backtraces with FFI callbacks.
*** bugfix: x86: lj_math_random_step() clobbers XMM regs on OSX Clang.
*** bugfix: fixed debug info for main chunk of stripped bytecode.
* upgraded LuaRestyCoreLibrary to 0.0.8.
** feature: [[resty.core.regex|https://github.com/openresty/lua-resty-core#restycoreregex]]: use {{{resty.lrucache}}} for the compiled regex cache for [[ngx.re.find|https://github.com/openresty/lua-nginx-module#ngxrefind]] and [[ngx.re.match|https://github.com/openresty/lua-nginx-module#ngxrematch]] in order to prevent pathalogical performance when the number of regexes has exceeded [[lua_regex_cache_max_entries|https://github.com/openresty/lua-nginx-module/#lua_regex_cache_max_entries]].
** optimize: [[resty.core.regex|https://github.com/openresty/lua-resty-core#restycoreregex]]: removed one obsolete assertion that was for a LuaJIT bug (already fixed).
* upgraded LuaRestyDNSLibrary to 0.12.
** feature: added support for the SRV resource record type (see [[RFC 2782|http://www.ietf.org/rfc/rfc2782.txt]]). thanks Torbjörn Norinder for the patch.
* upgraded LuaRestyUpstreamHealthcheckLibrary to 0.02.
** bugfix: for bad status lines, we could throw out the &quot;bad argument #2 to 'sub'&quot; error, reported by George Bashi.
** doc: avoided using the {{{\r\n}}} sequence in Lua long brackets because Lua would squeeze it to {{{\n}}}, unfortunately. thanks George Bashi for the report.
** doc: made it clear that multiple {{{upstream {} }}} blocks' checkers can share a single shm zone. thanks Robert Paprocki for asking.
** doc: now we need to turn off [[lua_socket_log_errors|https://github.com/openresty/lua-nginx-module/#lua_socket_log_errors]] explicitly in code examples.
* upgraded LuaRestyLrucacheLibrary to 0.02.
** feature: added an alternative implementation using ~FFI-based hash-table in the form of the new class {{{resty.lrucache.pureffi}}}, which is much faster than the default {{{resty.lrucache}}} class when there are a lot of key variations. thanks Shuxin Yang for the patch.
* upgraded LuaNginxModule to 0.9.10.
** feature: [[stream-typed cosockets|https://github.com/openresty/lua-nginx-module#ngxsockettcp]] are now full-duplex: a reader &quot;[[light thread|https://github.com/openresty/lua-nginx-module#ngxthreadspawn]]&quot; and a writer &quot;light thread&quot; can operate on the same cosocket simultaneously. thanks shun zhang and aviramc for the original patches.
** feature: added new API function [[ngx.thread.kill()|https://github.com/openresty/lua-nginx-module/#ngxthreadkill]] for killing a user &quot;light thread&quot;. thanks aviramc for the original patch.
** bugfix: the &quot;coroutine&quot; module table introduced by {{{require('coroutine')}}} was not working in our Lua context. thanks Paul K and ~Pierre-Yves Gérardy for the report.
** bugfix: fixed the initial size of the ngx.worker table and the misleading comment due to a copy&amp;paste mistake. thanks Suraj Jaiswal for the report.
** bugfix: the &quot;coctx cleanup&quot; handler might not be called before being overidden by other operations. this could happen when failing to yield in an error handler (for [[xpcall|http://www.lua.org/manual/5.1/manual.html#pdf-xpcall]]).
** bugfix: fixed an incorrect error message. thanks doujiang for the patch.
** bugfix: fixed a compilation error regression when using the Microsoft Visual C/C++ compiler. thanks itpp16 for the patch.
** bugfix: we should use {{{c-&gt;buffered &amp; NGX_HTTP_LOWLEVEL_BUFFERED}}} instead of {{{c-&gt;buffered}}} for testing if the downstream connection is busy writing.
** bugfix: we did not handle an out-of-memory case in [[ngx.req.set_body_data()|http://wiki.nginx.org/HttpLuaModule#ngx.req.set_body_data]].
** bugfix: ngx_http_lua_chain_get_free_buf(): avoided returning zero-sized memory bufs.
** bugfix: [[body_filter_by_lua*|https://github.com/openresty/lua-nginx-module#body_filter_by_lua]]: we might incorrectly pass zero-size bufs (in the form of &quot;special sync bufs&quot;) at the beginning of a chain, which could get stuck in the buffer of {{{ngx_http_writer_filter_module}}} (or in other words, being &quot;busy&quot;) while could still get recycled in the content handler (like [[content_by_lua|https://github.com/openresty/lua-nginx-module#content_by_lua]]), leading to buffer corruptions. thanks westhood for the report and patch.
** bugfix: we did not clear all the fields in the {{{ngx_buf_t}}} C struct when recycling chain link buffers.
** bugfix: the {{{*_by_lua_file}}} directives failed to load .lua files of exactly the size {{{n*LUAL_BUFFERSIZE}}} bytes with the error &quot;'end' expected (to close 'function' at line 1) near '&lt;eof&gt;'&quot;. thanks kworr for the report.
** change: now we always iterate through all the user light threads to ensure all threads are de-anchored even when the &quot;uthreads&quot; counter gets out of sync. also added an assertion on the &quot;uthreads&quot; counter.
** change: now we turn off our C-land assertions by default unless the user explicitly specifies the C compiler option {{{-DNGX_LUA_USE_ASSERT}}}.
** change: throw out the &quot;no memory&quot; Lua error consistently (instead of &quot;out of memory&quot;) when failing to allocate on the nginx side.
** change: we now still call {{{ngx_pfree()}}} in our own {{{pcre_free}}} hook.
** doc: documented the {{{NGX_LUA_USE_ASSERT}}} and {{{NGX_LUA_ABORT_AT_PANIC}}} C macros.
** doc: added performance notes to the sections for the [[ngx.var|https://github.com/openresty/lua-nginx-module#ngxvarvariable]] and [[ngx.ctx|https://github.com/openresty/lua-nginx-module#ngxctx]] API.
** doc: documented the types of Lua values that can be passed to the [[ngx.timer|https://github.com/openresty/lua-nginx-module#ngxtimerat]] callback functions.
* upgraded FormInputNginxModule to 0.09.
** bugfix: fixed warnings from the Microsoft Visual C/C++ compiler. thanks itpp16 for the report.
* upgraded EchoNginxModule to 0.54.
** bugfix: the &quot;unknown option for echo_subrequest_async&quot; error was thrown when Nginx variables were used in both the &quot;method&quot; argument and URI argument of the [[echo_subrequest|https://github.com/openresty/echo-nginx-module#echo_subrequest]] directive (and etc). thanks Utkarsh Upadhyay for the report.
** bugfix: fixed a misleading error message.
* upgraded SrcacheNginxModule to 0.28.
** feature: log an error message when [[srcache_store|http://wiki.nginx.org/HttpSRCacheModule#srcache_store]] subrequest has an error or returns a bad HTTP status code. thanks Yann Coleu for the report.
** doc: typo fix from javasboy.
* upgraded MemcNginxModule to 0.15.
** bugfix: we did not log error messages for invalid values of {{{$memc_flags}}}, {{{$memc_exptime}}}, and {{{$memc_value}}}, leading to hard-to-debug HTTP 400 status errors. thanks Yann Coleu for the report.
* bugfix: {{{./configure --without-lua_resty_dns}}} did not work as declared. thanks Vitaly for the report.
* bugfix: use {{{cc}}} as the default C compiler for LuaJIT and Lua C libraries because modern ~FreeBSD 10 has no gcc by default and its clang is already featureful enough to compile everything. thanks Stefan Parvu for the suggestion.
* change: {{{./configure --with-debug}}} now also passes the extra C compiler options {{{-DNGX_LUA_USE_ASSERT -DNGX_LUA_ABORT_AT_PANIC}}} to the LuaNginxModule build.
See ChangeLog1007000 for change log for OpenResty 1.7.0.x.</pre>
</div>
<div title="ChangeLog1007004" creator="YichunZhang" modifier="YichunZhang" created="201410100001" changecount="1">
<pre>&lt;&lt;toolbar permalink&gt;&gt;
! Version 1.7.4.1 - 9 October 2014
* upgraded the Nginx core to 1.7.4.
** see the changes here: http://nginx.org/en/CHANGES
* feature: added a new command-line utility, {{{resty}}}, to run Lua code or Lua files (for OpenResty) directly from the command-line. it is installed into the &quot;&lt;prefix&gt;/bin&quot; directory. prodded by Vitaly Kosenko. This tool is currently experimental.
* bugfix: {{{./configure}}}: we might misuse the homebrew version of LuaJIT on Mac OS X when the user specified the {{{--with-ld-opt=&quot;-L/usr/local/lib&quot;}}} option. thanks Aapo Talvensaari for the report.
* bugfix: {{{util/install}}}: remove the target file before overwriting to prevent running processes (if any) from crashing.
* bugfix: {{{./configure}}}: call &quot;sh&quot; explicitly for nginx's {{{./configure}}} script to prevent potential file permission issues.
* optimize: now we use the C compiler option {{{-O2}}} for everything by default (we used to use {{{-O1}}} which is too conservative).
* upgraded PostgresNginxModule to 1.0rc4.
** bugfix: segmentation fault might happen in {{{ngx_destroy_pool}}} when debug logging was enabled in the nginx build. thanks buddy-ekb for the report.
* upgraded EchoNginxModule to 0.56.
**  bugfix: our {{{create_loc_conf}}} callback did not return NULL on error. thanks Markus Linnala for the patch.
** bugfix: reading [[$echo_client_request_headers|https://github.com/openresty/echo-nginx-module#echo_client_request_headers]] would return garbled data when LF instead of CRLF is used as the line terminator in the original header.
** bugfix: reading [[$echo_client_request_headers|https://github.com/openresty/echo-nginx-module#echo_client_request_headers]] could lead to buffer overflow due to misuse of {{{r-&gt;header_end}}} while modules like ngx_fastcgi and ngx_proxy can change {{{r-&gt;header_end}}} to point to buffers of their own.
* upgraded FormInputNginxModule to 0.10.
** bugfix: &quot;pcre_exec -2&quot; error might happen when the standard &quot;if&quot; directive is used to test the empty value nginx variables set by this module with a regex. (Jiale)
** bugfix: we incorrectly overrode {{{r-&gt;read_event_handler}}} with {{{ngx_http_request_empty_handler}}} in our &quot;post read&quot; callback for client request body reading, which could waste CPU time in level-triggered event models like poll and select. thanks chen for the catch.
* upgraded SetMiscNginxModule 0.26.
** change: [[set_escape_uri|https://github.com/openresty/set-misc-nginx-module#set_escape_uri]]: use uppercase hexadecimal digits for percent-encoding as per RFC 3986. thanks splitice for the original patch.
**  bugfix: our {{{create_loc_conf}}} callback did not return NULL on error. thanks Markus Linnala for the patch.
** bugfix: fixed source and test files' permission. they should not be executable at all. thanks Christos Kontas for the report.
* upgraded LuaJIT to v2.1-20140805: https://github.com/openresty/luajit2/tags
** imported Mike Pall's latest bug fixes:
*** FFI: Fix {{{__index}}}/{{{__newindex}}} metamethod resolution for ctypes.
*** Invalidate backpropagation cache after DCE.
* upgraded LuaNginxModule to 0.9.12.
** feature: implemented the SSL/TLS cosocket API.
*** added new method [[sslhandshake()|https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake]] to the [[stream-typed cosocket|https://github.com/openresty/lua-nginx-module#ngxsockettcp]] objects.
*** added new configuration directives [[lua_ssl_trusted_certificate|https://github.com/openresty/lua-nginx-module#lua_ssl_trusted_certificate]], [[lua_ssl_verify_depth|https://github.com/openresty/lua-nginx-module#lua_ssl_verify_depth]], [[lua_ssl_crl|https://github.com/openresty/lua-nginx-module#lua_ssl_crl]], [[lua_ssl_protocols|https://github.com/openresty/lua-nginx-module#lua_ssl_protocols]], and [[lua_ssl_ciphers|https://github.com/openresty/lua-nginx-module#lua_ssl_ciphers]]. thanks aviramc for the original patch.
** feature: the standard coroutine API is now enabled in the context of [[header_filter_by_lua*|https://github.com/openresty/lua-nginx-module#header_filter_by_lua]] and [[body_filter_by_lua*|https://github.com/openresty/lua-nginx-module#body_filter_by_lua]]. thanks ngo for the request.
** feature: for content/rewrite/access_by_lua_file directives, we now return 404 status code instead of 500 in case that the specified .lua file cannot be opened. thanks Sam Lee for the suggestion.
** feature: added pure C API function for ~FFI-based implementation of reading [[ngx.header.HEADER|https://github.com/openresty/lua-nginx-module#ngxheaderheader]].
** feature: now we also explicitly check the Lua ABI/language version in our feature test of the {{{./configure}}} phase for a usable Lua lib.
** feature: added pure C API functions for ~FFI-based implementations of [[ngx.worker.pid()|https://github.com/openresty/lua-nginx-module#ngxworkerpid]] and [[ngx.worker.exiting()|https://github.com/openresty/lua-nginx-module#ngxworkerexiting]].
** bugfix: [[ngx.req.raw_header()|https://github.com/openresty/lua-nginx-module#ngxreqraw_header]] could lead to buffer overflow and the &quot;userdata length overflow&quot; error due to misuse of {{{r-&gt;header_end}}} while modules like ngx_fastcgi and ngx_proxy can change {{{r-&gt;header_end}}} to point to buffers of their own. thanks sadmedved for the report.
** bugfix: [[ngx.req.raw_header()|https://github.com/openresty/lua-nginx-module#ngxreqraw_header]] would return garbled data when LF instead of CRLF is used as the line terminator in the original header.
** bugfix: [[body_filter_by_lua*|https://github.com/openresty/lua-nginx-module#body_filter_by_lua]]: reading {{{ngx.arg[1]}}} after clearing {{{ngx.arg[1]}}} (by assigning nil or &quot;&quot;) could lead to segmentation faults. this regression had appeared in v0.9.10. thanks Jason Stangroome for the report.
** bugfix: [[init_worker_by_lua*|https://github.com/openresty/lua-nginx-module#init_worker_by_lua_file]] would conflict with some other nginx C modules (like ngx_proxy) when their {{{merge_loc_conf}}} callbacks (or alike) produce side-effects in {{{cf-&gt;cycle}}}. thanks Ruoshan Huang for the report.
** bugfix: [[stream-typed cosocket|https://github.com/openresty/lua-nginx-module#ngxsockettcp]] might read uninitialized memory bytes when logging errors due to sending to or receiving from a closed socket.
** bugfix: the stream-typed and datagram-typed cosockets' resolver handler did not handle some special errors correctly.
** bugfix: [[ngx.resp.get_headers()|https://github.com/openresty/lua-nginx-module#ngxrespget_headers]]: sometimes we might omit the builtin-headers ~Content-Type, ~Content-Length, Connection, and ~Transfer-Encoding. thanks Jon Keys for the report.
** bugfix: [[ngx.req.socket(true)|https://github.com/openresty/lua-nginx-module#ngxreqsocket]]: it incrrectly returned the error &quot;chunked request bodies not supported yet&quot; for raw request sockets with chunked request bodies. thanks Xiaofei Yang for the report.
** bugfix: we did not check allocation failures while compiling the pattern for [[tcpsock:receiveuntil()|https://github.com/openresty/lua-nginx-module#tcpsockreceiveuntil]]. thanks Tatsuhiko Kubo for the patch.
** bugfix: we did not use {{{lua_checkstack()}}} to prevent Lua stack overflow in our own C-land Lua backtrace generator.
** bugfix: fixed an incorrect error message. thanks aviramc for the patch.
** bugfix: for statically linked LuaJIT, we need to pass {{{-ldl}}} to the linker. thanks cf2012 for the report.
** bugfix: the [[tcp_nodelay|http://nginx.org/en/docs/http/ngx_http_core_module.html#tcp_nodelay]] directive configuration was not honored by upstream TCP cosockets, which could lead to extra delays for small messages. thanks Shun Zhang for reporting this issue.
** bugfix: fixed build failures with ~OpenSSL older than 0.9.8f. thanks FFCZ for the report.
** bugfix: compilation failed with nginx 1.3.6 or older. this regression had appeared in the v0.9.11 release.
** bugfix: compilation failed with nginx 0.9.x.
** bugfix: our {{{create_loc_conf}}} callback did not return NULL on error.
** bugfix: added allocation failure check for {{{ngx_array_init()}}} on the C land. thanks Tatsuhiko Kubo for the patch.
** optimize: we now cache the userdata metatable (for the {{{__gc}}} metamethod) in the lua registry for both the stream-typed datagram-typed cosockets.
** optimize: reading [[ngx.header.HEADER|https://github.com/openresty/lua-nginx-module#ngxheaderheader]]: eliminated dynamic allocations and data copying when there is no need to ransform &quot;_&quot; to &quot;-&quot; in the header name.
** change: [[ngx.escape_uri()|https://github.com/openresty/lua-nginx-module#ngxescape_uri]] now uses uppercase hexadecimal digits for percent-encoding according to the recommendation in RFC 3986. thanks Piotr Sikora for the suggestion.
** change: use the type {{{ngx_http_lua_ffi_str_t}}} instead of {{{ngx_str_t}}} in the pure C API function {{{ngx_http_lua_ffi_req_get_headers}}}.
** change: renamed the C macro {{{NGX_HTTP_LUA_NO_FFI_API}}} to {{{NGX_LUA_NO_FFI_API}}}.
** style: various coding style fixes and minor optimizations from Tatsuhiko Kubo.
** doc: documented the behavior of [[init_by_lua*|https://github.com/openresty/lua-nginx-module#init_by_lua]] when [[lua_code_cache|https://github.com/openresty/lua-nginx-module#lua_code_cache]] is off.
** doc: fixed a wrong statement regarding {{{require()}}} in the &quot;Lua Variable Scope&quot; section. thanks Hungpu DU for the report.
** doc: more clarification in the docs for the &quot;res.truncated&quot; flag returned by [[ngx.location.capture()|https://github.com/openresty/lua-nginx-module#ngxlocationcapture]]. thanks Jon Keys for asking.
** doc: added missing method name &quot;get_keys&quot; under &quot;ngx.shared.DICT&quot; and also fixed the method order. thanks George Bashi for the patch.
** doc: markdown: fixed the &quot;Back to TOC&quot; links for the sections (&quot;Nginx API for Lua&quot; and &quot;Directives&quot;) with inlined TOC. thanks ~Pierre-Yves Gérardy and Simon Eskildsen for the reports.
** doc: improved the wording in the &quot;Lua Coroutine Yielding/Resuming&quot; section. thanks Hungpu DU for the report.
** doc: improved the wording of the documentation for [[ngx.req.clear_header()|https://github.com/openresty/lua-nginx-module#ngxreqclear_header]] to prevent ambiguity. thanks ~Christophe-Marie Duquesne for the report.
* upgraded LuaRestyCoreLibrary to 0.0.9.
** feature: implemented the reading part of [[ngx.header.HEADER|https://github.com/openresty/lua-nginx-module#ngxheaderheader]] with FFI.
** feature: implemented [[ngx.worker.pid()|https://github.com/openresty/lua-nginx-module#ngxworkerpid]] and [[ngx.worker.exiting()|https://github.com/openresty/lua-nginx-module#ngxworkerexiting]] with FFI.
* upgraded LuaRestyUpstreamHealthcheckLibrary to 0.03.
** optimize: timers in different nginx worker processes can go out of phase as time goes, resulting in duplicate test requests from different workers in the same check interval. thanks fancyrabbit for the report and fix.
* upgraded LuaRestyWebSocketLibrary to 0.04.
** feature: [[resty.websocket.client|https://github.com/openresty/lua-resty-websocket#restywebsocketclient]]: added support for the &quot;origin&quot; option to specify the value of the {{{Origin}}} request header. thanks woo for the original patch.
** bugfix: [[resty.websocket.client|https://github.com/openresty/lua-resty-websocket#restywebsocketclient]]: connection pooling was broken due to duplicate websocket handshakes. thanks woo for the patch.
** bugfix: fixed the {{{Sec-WebSocket-Protocol}}} header when the secondary protocols are specified. thanks woo for the report.
** doc: typo fixes from Laurent Arnoud.
* upgraded LuaRestyDNSLibrary to 0.13.
** bugfix: we did not parse the character-strings in the &quot;TXT&quot; record data. thanks Kevin Ingersoll for the report.
* upgraded LuaRestyMySQLLibrary to 0.15.
** feature: added new boolean-value options &quot;ssl&quot; and &quot;ssl_verify&quot; to the [[connect()|https://github.com/openresty/lua-resty-mysql#connect]] method connecting to ~MySQL via SSL.
* upgraded LuaCjsonLibrary to 2.1.0.2.
** bugfix: the Makefile had a bug that overwrites the existing {{{cjson.so}}} file in place which could cause already running processes with this {{{.so}}} file loaded to crash. thanks ywsample for the report.
See ChangeLog1007002 for change log for OpenResty 1.7.2.x.</pre>
</div>
<div title="ChangeLog1007007" creator="YichunZhang" modifier="YichunZhang" created="201412070207" changecount="1">
<pre>&lt;&lt;toolbar permalink&gt;&gt;
! Version 1.7.7.1 - 6 December 2014
* upgraded the Nginx core to 1.7.7.
** see the changes here: http://nginx.org/en/CHANGES
* bugfix: applied a patch to the nginx core to fix the memory invalid reads when exceeding the pre-configured limits in an {{{ngx_hash_t}}} hash table.
* bugfix: applied a patch to the nginx core to fix a memory invalid read regression introduced in nginx 1.7.5+'s resolver.
* ./configure: usage text: renamed {{{--with-luajit=PATH}}} to {{{--with-luajit=DIR}}}. thanks Dominic for the suggestion.
* feature: ./configure: added the default prefix value to the usage text.
* upgraded LuaJIT to v2.1-20141128:  https://github.com/openresty/luajit2/tags
** imported Mike Pall's latest changes:
*** feature: FFI: added {{{ffi.typeinfo()}}}. thanks to Peter Colberg.
*** bugfix: fixed snapshot #0 handling for traces with a stack check on entry. this bug might lead to bad register overwrites (and eventually segmentation faults in GC upon trace exits, at least).
*** bugfix: FFI: no meta fallback when indexing pointer to incomplete struct.
*** bugfix: fixed fused constant loads under high register pressure.
*** bugfix: fixed ~DragonFly build (unsupported). thanks to Robin Hahling, Alex Hornung, and Joris Giovannangeli.
*** bugfix: FFI: fixed initialization of unions of subtypes. thanks to Peter Colberg.
*** bugfix: FFI: Fix for cdata vs. non-cdata arithmetic and comparisons. thanks to Roman Tsisyk.
*** optimize: eliminated hmask guard for forwarded HREFK.
** debugging: added an (expensive) assertion to check GC objects in current stack upon trace exiting. thanks Mike Pall. only enabled when building with {{{-DLUA_USE_ASSERT}}}.
* upgraded LuaNginxModule to 0.9.13.
** optimize: reduced the pool size of a fake connection from the default pool size (16KB) to 128B, affecting [[init_worker_by_lua|https://github.com/openresty/lua-nginx-module#init_worker_by_lua]] and [[ngx.timer.at|https://github.com/openresty/lua-nginx-module#ngxtimerat]].
** optimize: made fake requests share their connection pools, affecting [[init_worker_by_lua|https://github.com/openresty/lua-nginx-module#init_worker_by_lua]] and [[ngx.timer.at|https://github.com/openresty/lua-nginx-module#ngxtimerat]].
** feature: the error logger used by ngx.timer.at handlers now outputs the &quot;client: xxx, server: xxx&quot; context info for the original (true) request creating the timer.
** feature: added nginx configuration file names and line numbers to the rewrite/access/content/log_by_lua directives' Lua chunk names in order to simplify debugging.
** feature: [[ngx.flush(true)|https://github.com/openresty/lua-nginx-module#ngxflush]] now returns the &quot;timeout&quot; and &quot;client aborted&quot; errors to the Lua land for the cases that writing to the client is timed out or the client closes the connection prematurely, respectively.
** feature: [[ngx.flush(true)|https://github.com/openresty/lua-nginx-module#ngxflush]] can now wait on delayed events due to nginx's [[limit_rate|http://nginx.org/en/docs/http/ngx_http_core_module.html#limit_rate]] config directive or [[$limit_rate|http://nginx.org/en/docs/http/ngx_http_core_module.html#var_limit_rate]] variable settings. thanks Shafreeck Sea for the original patch.
** bugfix: [[ngx.flush()|https://github.com/openresty/lua-nginx-module#ngxflush]], [[ngx.eof()|https://github.com/openresty/lua-nginx-module#ngxeof]], and some other things did not update busy/free chains after calling the output filters.
** bugfix: ngx_gzip/ngx_gunzip module filters might cause [[ngx.flush(true)|https://github.com/openresty/lua-nginx-module#ngxflush]] to hang until timeout for nginx 1.7.7+ (and some other old versions of nginx). thanks Maxim Dounin for the help.
** bugfix: [[ngx.get_phase()|https://github.com/openresty/lua-nginx-module#ngxget_phase]] did not work in the context of [[init_worker_by_lua*|https://github.com/openresty/lua-nginx-module#init_worker_by_lua]].
** bugfix: use of [[ngx.flush(true)|https://github.com/openresty/lua-nginx-module#ngxflush]] with the [[limit_rate|http://nginx.org/en/docs/http/ngx_http_core_module.html#limit_rate]] config directive or the [[$limit_rate|http://nginx.org/en/docs/http/ngx_http_core_module.html#var_limit_rate]] variable may hang the request forever for large volumn of output data. thanks Shafreeck Sea for the report.
** bugfix: compilation error when PCRE is disabled in the nginx build. thanks Ivan Cekov for the report.
** bugfix: when syslog was enabled in the [[error_log|http://nginx.org/en/docs/ngx_core_module.html#error_log]] directive for nginx 1.7.1+, use of [[init_worker_by_lua|https://github.com/openresty/lua-nginx-module#init_worker_by_lua]] or [[ngx.timer.at()|https://github.com/openresty/lua-nginx-module#ngxtimerat]] would lead to segmentation faults. thanks shun.zhang for the report.
** bugfix: fixed compilation error with nginx 1.7.5+ because nginx 1.7.5+ changes the API in the events subsystem. thanks Charles R. Portwood II and Mathieu Le Marec for the report.
** bugfix: [[ngx.req.raw_header()|https://github.com/openresty/lua-nginx-module#ngxreqraw_header]]: buffer overflow and the &quot;buffer error&quot; exception might happen for massively pipelined downstream requests. thanks Dane Knecht for the report.
** bugfix: [[ngx.req.raw_header()|https://github.com/openresty/lua-nginx-module#ngxreqraw_header]]: we might change nginx's internal buffer pointers, which might cause bad side-effects.
** doc: added a new section, [[Cocockets Not Available Everywhere|https://github.com/openresty/lua-nginx-module#cocockets-not-available-everywhere]], under the [[Known Issues|https://github.com/openresty/lua-nginx-module#known-issues]] section.
* upgraded LuaRestyDNSLibrary to 0.14.
** feature: added support for the SPF record type specified by RFC 4408. thanks Tom Fitzhenry for the patch.
* upgraded LuaRestyLrucacheLibrary to 0.03.
** feature: the [[get()|https://github.com/openresty/lua-resty-lrucache#get]] method now also returns the stale value as the second returned value if available.
* upgraded LuaRestyLockLibrary to 0.04.
** bugfix: the shared dictionary would incorrectly get unref'd for multiple times when the [[lock()|https://github.com/openresty/lua-resty-lock#lock]] and/or [[unlock()|https://github.com/openresty/lua-resty-lock#unlock]] methods are called more than once. thanks Peng Wu for the report and Dejiang Zhu for the patch.
* upgraded EchoNginxModule to 0.57.
** bugfix: [[$echo_client_request_headers|https://github.com/openresty/echo-nginx-module#echo_client_request_headers]]: buffer overflow and the &quot;buffer error&quot; exception might happen for massively pipelined downstream requests.
** bugfix: [[$echo_client_request_headers|https://github.com/openresty/echo-nginx-module#echo_client_request_headers]]: we might change nginx's internal buffer pointers, which might cause bad side-effects.
* upgraded DrizzleNginxModule to 0.1.8.
** bugfix: fixed compilation error with nginx 1.7.5+ because nginx 1.7.5+ changes the API in the events subsystem.
* upgraded PostgresNginxModule to 1.0rc5.
** bugfix: fixed compilation error with nginx 1.7.5+ because nginx 1.7.5+ changes the API in the events subsystem.
* upgraded CoolkitNginxModule to 0.2rc2.
** bugfix: compilation failed when PCRE was disabled in the nginx build.
** feature: added the &quot;$location&quot; variable, by Piotr Sikora.
* upgraded SetMiscNginxModule to 0.27.
** bugfix: bugfix: fixed build failure when {{{--with-mail_ssl_module}}} is specified while {{{--with-http_ssl_module}}} is not. thanks Xiaochen Wang for the report.
See ChangeLog1007004 for change log for OpenResty 1.7.4.x.</pre>
</div>
<div title="ChangeLog8054" creator="YichunZhang" modifier="YichunZhang" created="201106210431" modified="201107081123" changecount="25">
<pre>&lt;&lt;toolbar permalink&gt;&gt;
! Stable Release 0.8.54.9 - 8 July 2011
This release is the same as  0.8.54.9rc6.

The following components are bundled by this release:
* ~LuaJIT-2.0.0-beta8
* array-var-nginx-module-0.02
* auth-request-nginx-module-0.2
* drizzle-nginx-module-0.1.0
* echo-nginx-module-0.36
* encrypted-session-nginx-module-0.01
* form-input-nginx-module-0.07rc4
* headers-more-nginx-module-0.15
* iconv-nginx-module-0.10rc3
* lua-5.1.4
* memc-nginx-module-0.12
* nginx-0.8.54
* ngx_devel_kit-0.2.17
* ngx_lua-0.2.0
* ngx_postgres-0.8
* rds-json-nginx-module-0.11
* redis2-nginx-module-0.07rc5
* set-misc-nginx-module-0.21
* srcache-nginx-module-0.12rc6
* upstream-keepalive-nginx-module-0.3
* xss-nginx-module-0.03rc3
! Mainline Version 0.8.54.9rc6 - 8 July 2011
* upgraded ngx_echo to v0.36 and ngx_memc to v0.12.
! Mainline Version 0.8.54.9rc5 - 7 July 2011
* applied the subrequest loop fix patch from Maxim Dounin.
! Mainline Version 0.8.54.9rc4 - 6 July 2011
* upgraded ngx_rds_json to v0.11, ngx_headers_more to v0.15, and ngx_drizzle to v0.1.0.
! Mainline Version 0.8.54.9rc3 - 5 July 2011
* upgraded ngx_drizzle to 0.0.15rc14 and ngx_lua to 0.2.0.
! Mainline Version 0.8.54.9rc2 - 4 July 2011
* upgraded ngx_lua to 0.1.6rc18.
! Mainline Version 0.8.54.9rc1 - 3 July 2011
* upgraded ngx_redis2 to 0.07rc5.
! Stable Release 0.8.54.8 - 1 July 2011
This release is the same as  0.8.54.8rc2.

The following components are bundled by this release:
* ~LuaJIT-2.0.0-beta8
* array-var-nginx-module-0.02
* auth-request-nginx-module-0.2
* drizzle-nginx-module-0.0.15rc13
* echo-nginx-module-0.36rc6
* encrypted-session-nginx-module-0.01
* form-input-nginx-module-0.07rc4
* headers-more-nginx-module-0.15rc3
* iconv-nginx-module-0.10rc3
* lua-5.1.4
* memc-nginx-module-0.12rc2
* nginx-0.8.54
* ngx_devel_kit-0.2.17
* ngx_lua-0.1.6rc17
* ngx_postgres-0.8
* rds-json-nginx-module-0.11rc2
* redis2-nginx-module-0.07rc4
* set-misc-nginx-module-0.21
* srcache-nginx-module-0.12rc6
* upstream-keepalive-nginx-module-0.3
* xss-nginx-module-0.03rc3
! Mainline Version 0.8.54.8rc2 - 1 July 2011
* upgraded ngx_echo to 0.36rc6, ngx_lua to 0.1.6rc17, ngx_srcache to 0.12rc6, and ngx_redis2 to 0.07rc4.
! Mainline Version 0.8.54.8rc1 - 28 June 2011
* we no longer bundle libdrizzle because [[libdrizzle 1.0|https://launchpad.net/libdrizzle]] is distributed with the drizzle server and hard to separate.
* now ngx_drizzle is disabled by default. you need to enable it via the {{{--with-http_drizzle_module}}} option.
* added {{{--with-libdrizzle}}} option to specify the (lib)drizzle installation prefix.
! Stable Release 0.8.54.7 - 27 June 2011
* identical to 0.8.54.7rc5.
! Mainline Version 0.8.54.7rc5 - 27 June 2011
* we should preserve timestamps when copying bundle/ to build/ in ./configure script. this should fix luajit build on some systems.
! Mainline Version 0.8.54.7rc4 - 27 June 2011
upgraded ngx_xss to 0.03rc3, ngx_drizzle to 0.0.15rc11, ngx_memc to 0.12rc2, ngx_srcache to 0.12rc5, ngx_redis2 to 0.07rc3.
! Mainline Version 0.8.54.7rc3 - 27 June 2011
* upgraded LuaJIT to 2.0.0beta8, ngx_lua to 0.1.6rc15, and ngx_echo to 0.36rc4.
! Mainline Version 0.8.54.7rc2
* Upgraded [[LuaNginxModule]] to v0.1.6rc14 and [[HeadersMoreNginxModule]] to v0.15rc3.
! Mainline Version 0.8.54.7rc1
* Upgraded [[HeadersMoreNginxModule]] to v0.15rc2 and [[LuaNginxModule]] to v0.1.6rc13.
! Release 0.8.54.6
* Upgraded [[DrizzleNginxModule]] to 0.0.15rc10.
* Upgraded [[LuaNginxModule]] to 0.1.6rc12.
* Upgraded [[Redis2NginxModule]] to 0.07rc2.
! Release 0.8.54.5
* Upgraded [[EchoNginxModule]] to 0.36rc3.
* Upgraded [[LuaNginxModule]] to 0.1.6rc9.
</pre>
</div>
<div title="Changes" creator="YichunZhang" modifier="YichunZhang" created="201210172306" modified="201412070206" changecount="14">
<pre>&lt;&lt;toolbar permalink&gt;&gt;
* [[Release 1.7.7.x|ChangeLog1007007]]
* [[Release 1.7.4.x|ChangeLog1007004]]
* [[Release 1.7.2.x|ChangeLog1007002]]
* [[Release 1.7.0.x|ChangeLog1007000]]
* [[Release 1.5.12.x|ChangeLog1005012]]
* [[Release 1.5.11.x|ChangeLog1005011]]
* [[Release 1.5.8.x|ChangeLog1005008]]
* [[Release 1.4.3.x|ChangeLog1004003]]
* [[Release 1.4.2.x|ChangeLog1004002]]
* [[Release 1.4.1.x|ChangeLog1004001]]
* [[Release 1.2.8.x|ChangeLog1002008]]
* [[Release 1.2.7.x|ChangeLog1002007]]
* [[Release 1.2.6.x|ChangeLog1002006]]
* [[Release 1.2.4.x|ChangeLog1002004]]
* [[Release 1.2.3.x|ChangeLog1002003]]
* [[Release 1.2.1.x|ChangeLog1002001]]
* [[Release 1.0.15.x|ChangeLog1000015]]
* [[Release 1.0.11.x|ChangeLog1000011]]
* [[Release 1.0.10.x|ChangeLog1000010]]
* [[Release 1.0.9.x|ChangeLog1000009]]
* [[Release 1.0.8.x|ChangeLog1000008]]
* [[Release 1.0.6.x|ChangeLog1000006]]
* [[Release 1.0.5.x|ChangeLog1000005]]
* [[Release 1.0.4.x|ChangeLog1000004]]
* [[Release 0.8.54.x|ChangeLog8054]]
</pre>
</div>
<div title="ColorPalette" creator="YichunZhang" modifier="YichunZhang" created="201106220418" modified="201106220719" changecount="84">
<pre>Background: #fff
Foreground: #000
PrimaryPale: #effa72
PrimaryLight: #007E00
PrimaryMid: #007E00
PrimaryDark: #145D00
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #bd4
SecondaryDark: #04859d
TertiaryPale: #c9f76f
TertiaryLight: #60d5ac
TertiaryMid: #00a383
TertiaryDark: #666
Error: #f88
</pre>
</div>
<div title="Components" creator="YichunZhang" modifier="YichunZhang" created="201106210424" modified="201406072253" changecount="29">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

下面列表中的组件被用于构建 OpenResty。所有的组件可以被激活或禁止。
大部组件默认是激活的，也有部件不是。
[[LuaJIT]]、 [[DrizzleNginxModule]]、[[PostgresNginxModule]]和[[IconvNginxModule]] 默认是没有激活的。您需要通过以下选项在编译 [[OpenResty]]的时候将它们各自激活， {{{--with-luajit}}}、 {{{--with-http_drizzle_module}}}、 {{{--with-http_postgres_module}}}和 {{{--with-http_iconv_module}}} 。

* [[LuaJIT]]
* [[ArrayVarNginxModule]]
* [[AuthRequestNginxModule]]
* [[CoolkitNginxModule]]
* [[DrizzleNginxModule]]
* [[EchoNginxModule]]
* [[EncryptedSessionNginxModule]]
* [[FormInputNginxModule]]
* [[HeadersMoreNginxModule]]
* [[IconvNginxModule]]
* [[StandardLuaInterpreter]]
* [[MemcNginxModule]]
* [[Nginx]]
* [[NginxDevelKit]]
* [[LuaCjsonLibrary]]
* [[LuaNginxModule]]
* [[LuaRdsParserLibrary]]
* [[LuaRedisParserLibrary]]
* [[LuaRestyCoreLibrary]]
* [[LuaRestyDNSLibrary]]
* [[LuaRestyLockLibrary]]
* [[LuaRestyLrucacheLibrary]]
* [[LuaRestyMemcachedLibrary]]
* [[LuaRestyMySQLLibrary]]
* [[LuaRestyRedisLibrary]]
* [[LuaRestyStringLibrary]]
* [[LuaRestyUploadLibrary]]
* [[LuaRestyUpstreamHealthcheckLibrary]]
* [[LuaRestyWebSocketLibrary]]
* [[LuaUpstreamNginxModule]]
* [[PostgresNginxModule]]
* [[RdsCsvNginxModule]]
* [[RdsJsonNginxModule]]
* [[RedisNginxModule]]
* [[Redis2NginxModule]]
* [[SetMiscNginxModule]]
* [[SrcacheNginxModule]]
* [[XssNginxModule]]
</pre>
</div>
<div title="ContactUs" creator="YichunZhang" modifier="ZoomQuiet" created="201106210414" modified="201203070817" tags="abt" changecount="6">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

You're encouraged to send general questions regarding OpenResty and Nginx to the [[nginx mailing list|http://nginx.org/mailman/listinfo/nginx]].

Reporting bugs are encouraged to file tickets for the ngx_openresty project on GitHub:

https://github.com/agentzh/ngx_openresty/issues

Alternatively you can send mails privately to [[agentzh|http://agentzh.org]]: agentzh at gmail dot com.

     严正建议:&quot;去邮件列表沟通令众人受益;-)&quot;
+ 订阅: 请发邮件至 openresty+subscribe@googlegroups.com
+ 发言: 请发邮件到 openresty@googlegroups.com
+ 退订: 请发邮件至 openresty+unsubscribe@googlegroups.com
+ 详情: http://groups.google.com/group/openresty
+ 仓库: https://github.com/agentzh/ngx_openresty
+ 提案: https://github.com/agentzh/ngx_openresty/issues
+ 中文版维基: http://openresty.org/cn/
</pre>
</div>
<div title="CoolkitNginxModule" creator="YichunZhang" modifier="YichunZhang" created="201309300700" modified="201309300700" changecount="2">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

This module is collection of small and useful Nginx add-ons.

Project homepage and documentation: https://github.com/FRiCKLE/ngx_coolkit/

This module is enabled by default and could be disabled by passing {{{--without-http_coolkit_module}}} option to OpenResty's {{{./configure}}} script.
</pre>
</div>
<div title="DefaultTiddlers" creator="YichunZhang" modifier="YichunZhang" created="201106210357" modified="201106210600" tags="admin" changecount="3">
<pre>[[OpenResty]] [[About]]</pre>
</div>
<div title="DonateOnline" creator="ZoomQuiet" modifier="YichunZhang" created="201205291432" modified="201309262027" changecount="5">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

! 通过 ~PayPal 捐助

If you'd like to make a one-time donation to OpenResty, you can use ~PayPal to make it fast and easy.
如果感觉 OpenResty 给力,想进行小额捐助,可以通过 ~PayPal 快捷完成.

* ~PayPal 成员可以直接使用 ~PayPal 帐号登录,并点击以下按钮完成对的{{{agentzh@gmail.com}}} &quot;支付&quot; .
* 非~PayPal 成员, 可以使用国际信用卡帐号来登入 ~PayPal. 两样的,点击下述按钮,跟随页面提示,完成 &quot;支付&quot; .

不论哪种方式,只要通过 ~PayPal 都能方便的捐助。

&lt;html&gt;
&lt;form name=&quot;_xclick&quot; action=&quot;https://www.paypal.com/cgi-bin/webscr&quot; method=&quot;post&quot; target=&quot;_blank&quot;&gt;
&lt;input type=&quot;hidden&quot; name=&quot;cmd&quot; value=&quot;_xclick&quot;&gt;
&lt;input type=&quot;hidden&quot; name=&quot;business&quot; value=&quot;agentzh@gmail.com&quot;&gt;
&lt;input type=&quot;hidden&quot; name=&quot;item_name&quot; value=&quot;OpenResty Donation (in USD)&quot;&gt;
&lt;input type=&quot;hidden&quot; name=&quot;currency_code&quot; value=&quot;USD&quot;&gt;
&lt;input type=&quot;hidden&quot; name=&quot;amount&quot; value=&quot;&quot;&gt;
&lt;input type=&quot;image&quot; src=&quot;../donate_button_paypal_01.gif&quot; border=&quot;0&quot; name=&quot;submit&quot; alt=&quot;用 PayPal 捐助&quot;&gt;
&lt;/form&gt;
&lt;/html&gt;

! 通过 ~AliPay (支付宝) 捐助

如果身在中国大陆,
通常,更加方便的捐助渠道是通过 [[AliPay|http://www.alipay.com/]] 支付给 {{{agentzh@yahoo.cn}}} , 或是简单的点击以下按钮:

&lt;html&gt;
&lt;a href=&quot;http://me.alipay.com/agentzh&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;../donate-with-alipay.png&quot;&gt;&lt;/a&gt;
&lt;/html&gt;

任何数额都不胜感激！
</pre>
</div>
<div title="Download" creator="YichunZhang" modifier="YichunZhang" created="201106210425" modified="201412070206" tags="Resources" changecount="183">
<pre>&lt;&lt;toolbar permalink&gt;&gt;
!发布
!! 最新版
* [[ngx_openresty-1.7.7.1.tar.gz|http://openresty.org/download/ngx_openresty-1.7.7.1.tar.gz]] &amp;nbsp; 3.2MB &amp;nbsp; [[PGP|http://openresty.org/download/ngx_openresty-1.7.7.1.tar.gz.asc]] &amp;nbsp; [[Changes|ChangeLog1007007]] - 2014年12月6日
!! 历史遗留版
有的时候我们可能会需要较老的版本：
* [[ngx_openresty-1.7.4.1.tar.gz|http://openresty.org/download/ngx_openresty-1.7.4.1.tar.gz]] &amp;nbsp; 3.2MB &amp;nbsp; [[PGP|http://openresty.org/download/ngx_openresty-1.7.4.1.tar.gz.asc]] &amp;nbsp; [[Changes|ChangeLog1007004]] - 2014年10月9日
* [[ngx_openresty-1.7.2.1.tar.gz|http://openresty.org/download/ngx_openresty-1.7.2.1.tar.gz]] &amp;nbsp; 3.2MB &amp;nbsp; [[PGP|http://openresty.org/download/ngx_openresty-1.7.2.1.tar.gz.asc]] &amp;nbsp; [[Changes|ChangeLog1007002]] - 2014年7月12日
* [[ngx_openresty-1.7.0.1.tar.gz|http://openresty.org/download/ngx_openresty-1.7.0.1.tar.gz]] &amp;nbsp; 3.1MB &amp;nbsp; [[PGP|http://openresty.org/download/ngx_openresty-1.7.0.1.tar.gz.asc]] &amp;nbsp; [[Changes|ChangeLog1007000]] - 2014年6月7日
* [[ngx_openresty-1.5.12.1.tar.gz|http://openresty.org/download/ngx_openresty-1.5.12.1.tar.gz]] &amp;nbsp; 3.1MB &amp;nbsp; [[PGP|http://openresty.org/download/ngx_openresty-1.5.12.1.tar.gz.asc]] &amp;nbsp; [[Changes|ChangeLog1005012]] - 2014年4月29日
* [[ngx_openresty-1.5.11.1.tar.gz|http://openresty.org/download/ngx_openresty-1.5.11.1.tar.gz]] &amp;nbsp; 3.1MB &amp;nbsp; [[PGP|http://openresty.org/download/ngx_openresty-1.5.11.1.tar.gz.asc]] &amp;nbsp; [[Changes|ChangeLog1005011]] - 2014年3月30日
* [[ngx_openresty-1.5.8.1.tar.gz|http://openresty.org/download/ngx_openresty-1.5.8.1.tar.gz]] &amp;nbsp; 3.1MB &amp;nbsp; [[PGP|http://openresty.org/download/ngx_openresty-1.5.8.1.tar.gz.asc]] &amp;nbsp; [[Changes|ChangeLog1005008]] - 2014年1月10日
* [[ngx_openresty-1.4.3.6.tar.gz|http://openresty.org/download/ngx_openresty-1.4.3.6.tar.gz]] &amp;nbsp; 3.1MB &amp;nbsp; [[PGP|http://openresty.org/download/ngx_openresty-1.4.3.6.tar.gz.asc]] &amp;nbsp; [[Changes|ChangeLog1004003]] - 2013年11月20日
* [[ngx_openresty-1.4.3.4.tar.gz|http://openresty.org/download/ngx_openresty-1.4.3.4.tar.gz]] &amp;nbsp; 3.1MB &amp;nbsp; [[PGP|http://openresty.org/download/ngx_openresty-1.4.3.4.tar.gz.asc]] &amp;nbsp; [[Changes|ChangeLog1004003]] - 2013年11月12日
* [[ngx_openresty-1.4.2.8.tar.gz|http://openresty.org/download/ngx_openresty-1.4.2.8.tar.gz]] &amp;nbsp; 3.0MB &amp;nbsp; [[PGP|http://openresty.org/download/ngx_openresty-1.4.2.8.tar.gz.asc]] &amp;nbsp; [[Changes|ChangeLog1004002]] - 2013年9月22日
* [[ngx_openresty-1.2.8.6.tar.gz|http://openresty.org/download/ngx_openresty-1.2.8.6.tar.gz]] &amp;nbsp; 2.9MB &amp;nbsp; [[PGP|http://openresty.org/download/ngx_openresty-1.2.8.6.tar.gz.asc]] &amp;nbsp; [[Changes|ChangeLog1002008]] - 2013年6月10日
* [[ngx_openresty-1.2.7.8.tar.gz|http://openresty.org/download/ngx_openresty-1.2.7.8.tar.gz]] &amp;nbsp; 2.9MB &amp;nbsp; [[PGP|http://openresty.org/download/ngx_openresty-1.2.7.8.tar.gz.asc]] &amp;nbsp; [[Changes|ChangeLog1002007]] - 2013年5月13日
* [[ngx_openresty-1.2.7.6.tar.gz|http://openresty.org/download/ngx_openresty-1.2.7.6.tar.gz]] &amp;nbsp; 2.9MB &amp;nbsp; [[PGP|http://openresty.org/download/ngx_openresty-1.2.7.6.tar.gz.asc]] &amp;nbsp; [[Changes|ChangeLog1002007]] - 2013年4月17日
* [[ngx_openresty-1.2.6.6.tar.gz|http://openresty.org/download/ngx_openresty-1.2.6.6.tar.gz]] &amp;nbsp; 2.9MB &amp;nbsp; [[PGP|http://openresty.org/download/ngx_openresty-1.2.6.6.tar.gz.asc]] &amp;nbsp; [[Changes|ChangeLog1002006]] - 2013年2月17日
* [[ngx_openresty-1.2.4.14.tar.gz|http://openresty.org/download/ngx_openresty-1.2.4.14.tar.gz]] 2.9MB [[Changes|ChangeLog1002004]] - 2012年12月23日
* [[ngx_openresty-1.2.3.8.tar.gz|http://openresty.org/download/ngx_openresty-1.2.3.8.tar.gz]] 2.9MB [[Changes|ChangeLog1002003]] - 2012年10月8日
* [[ngx_openresty-1.0.11.28.tar.gz|http://openresty.org/download/ngx_openresty-1.0.11.28.tar.gz]] 2.7MB [[Changes|ChangeLog1000011]] - 25 March 2012
* [[ngx_openresty-1.0.10.48.tar.gz|http://openresty.org/download/ngx_openresty-1.0.10.48.tar.gz]] 2.6MB [[Changes|ChangeLog1000010]] - 1 February 2012
* [[ngx_openresty-1.0.10.44.tar.gz|http://openresty.org/download/ngx_openresty-1.0.10.44.tar.gz]] 2.5MB [[Changes|ChangeLog1000010]] - 16 January 2012
* [[ngx_openresty-1.0.10.24.tar.gz|http://openresty.org/download/ngx_openresty-1.0.10.24.tar.gz]] 2.5MB [[Changes|ChangeLog1000010]] - 11 December 2011
* [[ngx_openresty-1.0.9.10.tar.gz|http://openresty.org/download/ngx_openresty-1.0.9.10.tar.gz]] 2.5MB [[Changes|ChangeLog1000009]] - 16 November 2011
* [[ngx_openresty-1.0.8.26.tar.gz|http://openresty.org/download/ngx_openresty-1.0.8.26.tar.gz]] 2.5MB [[Changes|ChangeLog1000008]] - 3 November 2011
* [[ngx_openresty-1.0.6.22.tar.gz|http://openresty.org/download/ngx_openresty-1.0.6.22.tar.gz]] 2.4MB [[Changes|ChangeLog1000006]] - 7 October 2011

安装介绍参考: [[安装|Installation]]
</pre>
</div>
<div title="DrizzleNginxModule" creator="YichunZhang" modifier="YichunZhang" created="201106210822" modified="201108260112" changecount="14">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

This is an nginx upstream module that talks to ~MySQL and/or Drizzle database servers by [[libdrizzle]].

This ngx_drizzle module is not enabled by default. You should specify the {{{--with-http_drizzle_module}}} optiotn while configuring OpenResty.

The [[libdrizzle]] C library is no longer bundled by OpenResty. You need to download the drizzle server tarball from https://launchpad.net/drizzle.

When you get the drizzle7 release tarball, you can install libdrizzle-1.0 like this:
{{{
tar xzvf drizzle7-VERSION.tar.gz
cd drizzle7-VERSION/
./configure --without-server
make libdrizzle-1.0
make install-libdrizzle-1.0
}}}
where {{{VERSION}}} is the drizzle7 release version number like {{{2011.06.20}}}.

Please ensure that you have the {{{python}}} command point to a {{{python2}}} interpreter. It's known that on recent Arch Linux distribution, {{{python}}} is linked to {{{python3}}} by default, and while running {{{make libdrizzle-1.0}}} will yield the following error:
{{{
  File &quot;config/pandora-plugin&quot;, line 185
    print &quot;Dependency loop detected with %s&quot; % plugin['name']
                                           ^
SyntaxError: invalid syntax
make: *** [.plugin.scan] Error 1
}}}
You can fix this by pointing {{{python}}} temporarily to {{{python2}}}.

When you install the libdrizzle-1.0 library to a custom path prefix, you can specify the libdrizzle prefix to OpenResty like this:
{{{
cd /path/to/ngx_openresty-VERSION/
./configure --with-libdrizzle=/path/to/drizzle --with-http_drizzle_module
}}}

Documentation page: http://wiki.nginx.org/HttpDrizzleModule

Project page: https://github.com/chaoslawful/drizzle-nginx-module</pre>
</div>
<div title="DynamicRoutingBasedOnRedis" creator="YichunZhang" modifier="YichunZhang" created="201107270402" modified="201109141016" changecount="11">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

This sample demonstrates how to use redis to route incoming requests to different HTTP backends based on the requests' {{{User-Agent}}} header.

This demo uses the modules Redis2NginxModule, LuaNginxModule, LuaRedisParserLibrary, and SetMiscNginxModule bundled by OpenResty:

Here's the complete code listing for our {{{nginx.conf}}}:

{{{
worker_processes  1;
error_log logs/error.log info;

events {
    worker_connections 1024;
}

http {
    upstream apache.org {
        server apache.org;
    }

    upstream nginx.org {
        server nginx.org;
    }

    server {
        listen 8080;

        location = /redis {
            internal;
            set_unescape_uri $key $arg_key;
            redis2_query get $key;
            redis2_pass 127.0.0.1:6379;
        }

        location / {
            set $target '';
            access_by_lua '
                local key = ngx.var.http_user_agent
                local res = ngx.location.capture(
                    &quot;/redis&quot;, { args = { key = key } }
                )

                print(&quot;key: &quot;, key)

                if res.status ~= 200 then
                    ngx.log(ngx.ERR, &quot;redis server returned bad status: &quot;,
                        res.status)
                    ngx.exit(res.status)
                end

                if not res.body then
                    ngx.log(ngx.ERR, &quot;redis returned empty body&quot;)
                    ngx.exit(500)
                end

                local parser = require &quot;redis.parser&quot;
                local server, typ = parser.parse_reply(res.body)
                if typ ~= parser.BULK_REPLY or not server then
                    ngx.log(ngx.ERR, &quot;bad redis response: &quot;, res.body)
                    ngx.exit(500)
                end

                print(&quot;server: &quot;, server)

                ngx.var.target = server
            ';

            proxy_pass http://$target;
        }
    }
}
}}}

And then let's start the redis server on the localhost:6379:
{{{
$ ./redis-server  # default port is 6379
}}}

and feed some keys into this using the redis-cli utility:
{{{
   $ ./redis-cli
   redis&gt; set foo apache.org
   OK
   redis&gt; set bar nginx.org
   OK
}}}
And then let's test our nginx app!
{{{
   $ curl --user-agent foo localhost:8080
   &lt;apache.org home page goes here&gt;

   $ curl --user-agent bar localhost:8080
   &lt;nginx.org home page goes here&gt;
}}}
To further tune the performance, one could enable the connection pool for the redis connections, as documented in Redis2NginxModule's README.</pre>
</div>
<div title="EchoNginxModule" creator="YichunZhang" modifier="YichunZhang" created="201106210824" modified="201108260112" changecount="4">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

This module wraps lots of Nginx internal ~APIs for streaming input and output, parallel/sequential subrequests, timers and sleeping, as well as various meta data accessing.

Basically it provides various utilities that help testing and debugging of other modules by trivially emulating different kinds of faked subrequest locations.

Documentation: http://wiki.nginx.org/HttpEchoModule

Project page: https://github.com/agentzh/echo-nginx-module</pre>
</div>
<div title="EncryptedSessionNginxModule" creator="YichunZhang" modifier="YichunZhang" created="201106210834" modified="201106210839" changecount="3">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

This module provides encryption and decryption support for [[Nginx]] variables based on ~AES-256 with Mac.

This module is usually used with [[SetMiscNginxModule]] and the standard [[rewrite module|http://wiki.nginx.org/NginxHttpRewriteModule]]'s directives.

This module can be used to implement simple user login and access control of web applications.

Project page: http://github.com/agentzh/encrypted-session-nginx-module</pre>
</div>
<div title="FormInputNginxModule" creator="YichunZhang" modifier="YichunZhang" created="201106210901" changecount="1">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

This is an [[Nginx]] module that reads HTTP POST and PUT request body encoded in &quot;application/x-www-form-urlencoded&quot;, and parse the arguments in request body into [[Nginx]] variables.

Project page: https://github.com/calio/form-input-nginx-module
</pre>
</div>
<div title="GettingStarted" creator="YichunZhang" modifier="YichunZhang" created="201106201139" modified="201111030651" changecount="29">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

First of all, please go to the [[Download]] page to get the source code tarball of OpenResty, and see the [[Installation]] page for how to build and install it into your system.

!~HelloWorld
!!Prepare directory layout
We first create a separate directory for our experiments. You can use an arbitrary directory. Here for simplicity, we just use {{{~/work}}}:
{{{
mkdir ~/work
cd ~/work
mkdir logs/ conf/
}}}
Note that we've also created the {{{logs/}}} directory for logging files and {{{conf/}}} for our config files.
!!Prepare the nginx.conf config file
Create a simple plain text file named {{{conf/nginx.conf}}} with the following contents in it:
{{{
worker_processes  1;
error_log logs/error.log;
events {
    worker_connections 1024;
}
http {
    server {
        listen 8080;
        location / {
            default_type text/html;
            content_by_lua '
                ngx.say(&quot;&lt;p&gt;hello, world&lt;/p&gt;&quot;)
            ';
        }
    }
}
}}}
If you're familiar with Nginx configuration, it should look very familiar to you. OpenResty is just an enhanced version of Nginx by means of addon modules anyway. You can take advantage of all the exisitng goodies in the Nginx world.
!!Start the Nginx server
Assuming you have installed OpenResty into {{{/usr/local/openresty}}} (this is the default), we make our {{{nginx}}} executable of our OpenResty installation available in our {{{PATH}}} environment:
{{{
PATH=/usr/local/openresty/nginx/sbin:$PATH
export PATH
}}}
Then we start the nginx server with our config file this way:
{{{
nginx -p `pwd`/ -c conf/nginx.conf
}}}
Error messages will go to the stderr device or the default error log files {{{logs/error.log}}} in the current working directory.
!!Access our ~HelloWorld web service
We can use curl to access our new web service that says ~HelloWorld:
{{{
curl http://localhost:8080/
}}}
If everything is okay, we should get the output
{{{
&lt;p&gt;hello, world&lt;/p&gt;
}}}
You can surely point your favorite web browser to the location {{{http://localhost:8080/}}}.
!!Test performance
See [[Benchmark]] for details.

!Where to go from here

View the documentation of each component at the [[Components]] page and find Nginx related stuffs on the [[Nginx Wiki site|http://wiki.nginx.org/]].
</pre>
</div>
<div title="GitHub" creator="YichunZhang" modifier="YichunZhang" created="201106210420" modified="201106210420" changecount="2">
<pre>~GitHub is social coding network based on git, a very popular version control system. See its homepage: http://github.com.</pre>
</div>
<div title="HeadersMoreNginxModule" creator="YichunZhang" modifier="YichunZhang" created="201106210859" modified="201108260110" changecount="4">
<pre>This module allows you to add, set, or clear any response or request headers that you specify.

This is an enhanced version of the standard headers module because it provides more utilities like resetting or clearing &quot;builtin headers&quot; like ~Content-Type, ~Content-Length, and Server. 

Documentation: http://wiki.nginx.org/HttpHeadersMoreModule
Project page: http://github.com/agentzh/headers-more-nginx-module</pre>
</div>
<div title="IconvNginxModule" creator="YichunZhang" modifier="YichunZhang" created="201106210904" modified="201106220736" changecount="5">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

This is an [[Nginx]] module that uses libiconv to convert characters of different encoding. It brings the {{{set_iconv}}} and {{{iconv_filter}}} commands to [[Nginx]].

It can either process Nginx variables or process response bodies as an output filter.

This module is not enabled by default, and you need to specify the {{{--with-http_iconv_module}}} option while [[building OpenResty|Installation]]. This Nginx module requires libiconv to be installed into your system.

Use cases: http://forum.nginx.org/read.php?2,206658,207119

Project page: https://github.com/calio/iconv-nginx-module
</pre>
</div>
<div title="Installation" creator="YichunZhang" modifier="YourName" created="201106210440" modified="201112121504" changecount="51">
<pre>&lt;&lt;toolbar permalink&gt;&gt;
如果您还没有下载 OpenResty 的源码包l, 请到 [[Download]] 页下载。

首先，您可以根据下面的示例安装和构建OpenResty。
{{{
tar xzvf ngx_openresty-VERSION.tar.gz
cd ngx_openresty-VERSION/
./configure
make
make install
}}}
示例中的 {{{VERSION}}}替换成 OpenResty的版本号, 比如 {{{0.8.54.6}}}。
如果您在构建过程中需要对于细节更加灵活的控制，请您继续阅读。
!安装前的准备
您必须将这些库 {{{perl 5.6.1+}}}, {{{libreadline}}}, {{{libpcre}}}, {{{libssl}}}安装在您的电脑之中。 对于 Linux来说, 您需要确认使用 {{{ldconfig}}} 命令，让其在您的系统环境路径中能找到它们。
!!Debian 和 Ubuntu 用户
推荐您使用 apt-get安装以下的开发库:
{{{
apt-get install libreadline-dev libpcre3-dev libssl-dev perl
}}}
!!Fedora 和 ~RedHat 用户
推荐您使用yum安装以下的开发库:
{{{
yum install readline-devel pcre-devel openssl-devel
}}}
!!Mac OS X (Darwin) 用户
推荐您使用一些软件管理工具先安装PCRE, 比如说 [[Homebrew|http://mxcl.github.com/homebrew/]]:
{{{
brew install pcre
}}}
当然了，您也可以直接通过代码安装 PCRE  :)
!!~FreeBSD 用户
您需要安装以下的工具:
* devel/gmake
* security/openssl
* devel/pcre
!!Solaris 11 用户
您需要从官方的源中安装以下的工具:
* gcc-3
* ~SUNWlibm
通常情况下可以根据以下的示例安装：
{{{
pfexec pkg install gcc-3 SUNWlibm
}}}
!构建 ~OpenResty
!!下载
从下载页 [[Download]]下载最新的ngx_openresty源码包，并且像下面的示例一样将其解压:
{{{
tar xzvf ngx_openresty-VERSION.tar.gz
}}}
 {{{VERSION}}} 的地方替换成您下载的源码包的版本号，比如说 {{{0.8.54.6}}}。
!!./configure
然后在进入 {{{ngx_openresty-VERSION/}}} 目录, 然后输入以下命令配置:
{{{
./configure
}}}
默认, {{{--prefix=/usr/local/openresty}}} 程序会被安装到/usr/local/openresty目录。

您可以指定各种选项，比如
{{{
./configure --prefix=/opt/openresty \
            --with-luajit \
            --without-http_redis2_module \
            --with-http_iconv_module \
            --with-http_postgres_module
}}}
试着使用 {{{./configure --help}}} 查看更多的选项。

配置文件（./configure script）运行出错可以到 {{{build/nginx-VERSION/objs/autoconf.err}}} 找到。 {{{VERSION}}} 的地方必须与OpenResty版本号相对应, 比如 {{{0.8.54.6}}}。
!!!Solaris的用户请注意：
对于 Solaris,安装开发库一般通过 ~OpenSSL 的形式插入 {{{/lib}}}, 因此当编译时出现 missing ~OpenSSL 说明您已经安装过了t, 特别是一些选项的时候 {{{--with-ld-opt='-L/lib'}}} 。
!!make
您可以使用下面的命令来编译：
{{{
make
}}}
如果您的电脑支持多核 {{{make}}} 工作的特性, 您可以这样编译:
{{{
make -j2
}}}
假设您是的机器是双核。
!!make install
如果前面的步骤都没有问题的话,您可以使用下面的命令安装l OpenResty到您的系统之中：
{{{
make install
}}}
在 Linux,通常包括 {{{sudo}}}来执行root权限做的事情。
</pre>
</div>
<div title="LuaCjsonLibrary" creator="YichunZhang" modifier="YichunZhang" created="201108110208" changecount="1">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

Lua CJSON is a Lua C module that provides fast JSON parsing and encoding support for Lua.

Project homepage: http://www.kyne.com.au/~mark/software/lua-cjson.php
</pre>
</div>
<div title="LuaJIT" creator="YichunZhang" modifier="YichunZhang" created="201106210816" modified="201106210911" changecount="6">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

LuaJIT is a ~Just-In-Time Compiler for the Lua programming language. 

Homepage: http://luajit.org/luajit.html

LuaJIT is not enabled by default. Please specify the {{{--with-luajit}}} option while configuring OpenResty while building it. See [[Installation]] for details.
</pre>
</div>
<div title="LuaNginxModule" creator="YichunZhang" modifier="YichunZhang" created="201106210826" modified="201108260110" changecount="4">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

This module embeds the Lua interpreter or LuaJIT into the nginx core and integrates the powerful Lua threads (aka Lua coroutines) into the nginx event model by means of nginx subrequests.

Unlike Apache's mod_lua and Lighttpd's mod_magnet, Lua code written atop this module can be 100% non-blocking on network traffic as long as you use the ngx.location.capture or ngx.location.capture_multi interfaces to let the nginx core do all your requests to mysql, postgresql, memcached, upstream http web services, and etc etc etc (see ngx_drizzle, ngx_postgres, ngx_memc, and ngx_proxy modules for details).

The Lua interpreter instance is shared across all the requests in a single nginx worker process.

Request contexts are isolated from each other by means of Lua (lightweight) threads (aka Lua coroutines). And Lua modules loaded are persistent on the nginx worker process level. So the memory footprint is quite small even when your nginx worker process is handling 10K requests at the same time.

Documentation: http://wiki.nginx.org/HttpLuaModule

Project page: http://github.com/chaoslawful/lua-nginx-module</pre>
</div>
<div title="LuaRdsParserLibrary" creator="YichunZhang" modifier="YichunZhang" created="201108310738" modified="201108310740" changecount="5">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

This Lua library can be used to parse the ~Resty-DBD-Stream formatted data generated by DrizzleNginxModule and PostgresNginxModule into Lua data structures. In the past, we have to use JSON as the intermediate data format which is quite inefficient in terms of both memory and CPU time.

To maximize speed and minimize memory footprint, this library is implemented in pure C.

This library is enabled by default; use the {{{--without-lua_rds_parser}}} option to disable it when running {{{./configure}}} to build OpenResty.

Project homepage: https://github.com/agentzh/lua-rds-parser
</pre>
</div>
<div title="LuaRedisParserLibrary" creator="YichunZhang" modifier="YichunZhang" created="201108110210" modified="201108281337" changecount="3">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

The lua-redis-parser library implements a thin and fast redis raw response parser that constructs corresponding lua data strucutres, as well as a function that constructs redis raw requests.

Documentation page: http://wiki.nginx.org/LuaRedisParser

Project homepage: https://github.com/agentzh/lua-redis-parser
</pre>
</div>
<div title="LuaRestyCoreLibrary" creator="YichunZhang" modifier="YichunZhang" created="201312150017" changecount="1">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

Reimplements the Lua API provided by LuaNginxModule with LuaJIT FFI.

Project homepage: https://github.com/agentzh/lua-resty-core

This library is enabled by default. You can specify the {{{--without-lua_resty_core}}} option to OpenResty's {{{./configure}}} script to explicitly disable it.</pre>
</div>
<div title="LuaRestyDNSLibrary" creator="YichunZhang" modifier="YichunZhang" created="201211120214" changecount="1">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

Nonblocking DNS (Domain Name System) resolver for LuaNginxModule based on the cosocket API.

Project homepage: https://github.com/agentzh/lua-resty-dns

This library is enabled by default. You can specify the {{{--without-lua_resty_dns}}} option to OpenResty's {{{./configure}}} script to explicitly disable it.</pre>
</div>
<div title="LuaRestyLockLibrary" creator="YichunZhang" modifier="YichunZhang" created="201309300658" changecount="1">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

This Lua library implements a simple nonblocking mutex lock API based on LuaNginxModule's shared memory dictionaries. Mostly useful for eliminating &quot;dog-pile effects&quot;.

Project homepage: https://github.com/agentzh/lua-resty-lock

This library is enabled by default. You can specify the {{{--without-lua_resty_lock}}} option to OpenResty's {{{./configure}}} script to explicitly disable it.</pre>
</div>
<div title="LuaRestyLrucacheLibrary" creator="YichunZhang" modifier="YichunZhang" created="201406072254" changecount="1">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

Implements a Lua-land LRU cache for OpenResty.

Project homepage: https://github.com/openresty/lua-resty-lrucache

This library is enabled by default. You can specify the {{{--without-lua_resty_lrucache}}} option to OpenResty's {{{./configure}}} script to explicitly disable it.</pre>
</div>
<div title="LuaRestyMemcachedLibrary" creator="YichunZhang" modifier="YichunZhang" created="201210172304" changecount="1">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

Lua [[Memcached|http://memcached.org/]] client driver for LuaNginxModule based on the cosocket API.

Project homepage: https://github.com/agentzh/lua-resty-memcached

This library is enabled by default. You can specify the {{{--without-lua_resty_memcached}}} option to OpenResty's {{{./configure}}} script to explicitly disable it.
</pre>
</div>
<div title="LuaRestyMySQLLibrary" creator="YichunZhang" modifier="YichunZhang" created="201210172304" changecount="1">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

Lua [[MySQL|http://en.wikipedia.org/wiki/MySQL]] client driver for LuaNginxModule based on the cosocket API.

Project homepage: https://github.com/agentzh/lua-resty-mysql

This library is enabled by default. You can specify the {{{--without-lua_resty_mysql}}} option to OpenResty's {{{./configure}}} script to explicitly disable it.
</pre>
</div>
<div title="LuaRestyRedisLibrary" creator="YichunZhang" modifier="YichunZhang" created="201210172303" changecount="1">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

Lua [[Redis|http://redis.io/]] client driver for LuaNginxModule based on the cosocket API.

Project homepage: https://github.com/agentzh/lua-resty-redis

This library is enabled by default. You can specify the {{{--without-lua_resty_redis}}} option to OpenResty's {{{./configure}}} script to explicitly disable it.
</pre>
</div>
<div title="LuaRestyStringLibrary" creator="YichunZhang" modifier="YichunZhang" created="201211120215" changecount="1">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

A Lua library that provides string utilities and common hash functions for LuaNginxModule.

Project homepage: https://github.com/agentzh/lua-resty-string

This library is enabled by default. You can specify the {{{--without-lua_resty_string}}} option to OpenResty's {{{./configure}}} script to explicitly disable it.
</pre>
</div>
<div title="LuaRestyUploadLibrary" creator="YichunZhang" modifier="YichunZhang" created="201211120213" changecount="1">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

Streaming reader and parser for HTTP file uploading based on LuaNginxModule's cosocket API.

Project homepage: https://github.com/agentzh/lua-resty-upload

This library is enabled by default. You can specify the {{{--without-lua_resty_upload}}} option to OpenResty's {{{./configure}}} script to explicitly disable it.
</pre>
</div>
<div title="LuaRestyUpstreamHealthcheckLibrary" creator="YichunZhang" modifier="YichunZhang" created="201403310513" changecount="1">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

Health Checker for Nginx Upstream Servers in Pure Lua.

Project homepage: https://github.com/agentzh/lua-resty-upstream-healthcheck

This library is enabled by default. You can specify the {{{--without-lua_resty_upstream_healthcheck}}} option to OpenResty's {{{./configure}}} script to explicitly disable it.
</pre>
</div>
<div title="LuaRestyWebSocketLibrary" creator="YichunZhang" modifier="YichunZhang" created="201309300658" changecount="1">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

This Lua library implements both a nonblocking ~WebSocket server and a nonblocking ~WebSocket client based on LuaNginxModule's cosocket API.

Project homepage: https://github.com/agentzh/lua-resty-websocket

This library is enabled by default. You can specify the {{{--without-lua_resty_websocket}}} option to OpenResty's {{{./configure}}} script to explicitly disable it.</pre>
</div>
<div title="LuaUpstreamNginxModule" creator="YichunZhang" modifier="YichunZhang" created="201403310514" changecount="1">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

This Nginx C module exposes a Lua API to LuaNginxModule for classic Nginx upstreams.

Documentation: https://github.com/agentzh/lua-upstream-nginx-module#readme

Project page: https://github.com/agentzh/lua-upstream-nginx-module</pre>
</div>
<div title="MainMenu" creator="YichunZhang" modifier="YichunZhang" created="201106210357" modified="201210172306" tags="admin" changecount="22">
<pre>[[概览|OpenResty]]
[[组件|Components]]
[[新手上路|GettingStarted]]
[[样例|Samples]]
[[性能评测|Benchmark]]
[[幻灯片|Presentations]]
[[电子图书|eBooks]]
[[资源|Resources]]
[[下载|Download]]
[[安装|Installation]]
[[变更历史|Changes]]
[[测试|QualityAssurance]]
[[联系我们|ContactUs]]
[[捐助|DonateOnline]]
[[关于|About]]

[img[feed-icon-14x14.png]] [[RSS 订阅|RSSFeed]]

&lt;html&gt;
&lt;form name=&quot;_xclick&quot; action=&quot;https://www.paypal.com/cgi-bin/webscr&quot; method=&quot;post&quot; target=&quot;_blank&quot;&gt;
&lt;input type=&quot;hidden&quot; name=&quot;cmd&quot; value=&quot;_xclick&quot;&gt;
&lt;input type=&quot;hidden&quot; name=&quot;business&quot; value=&quot;agentzh@gmail.com&quot;&gt;
&lt;input type=&quot;hidden&quot; name=&quot;item_name&quot; value=&quot;OpenResty Donation (in USD)&quot;&gt;
&lt;input type=&quot;hidden&quot; name=&quot;currency_code&quot; value=&quot;USD&quot;&gt;
&lt;input type=&quot;hidden&quot; name=&quot;amount&quot; value=&quot;&quot;&gt;
&lt;input type=&quot;image&quot; src=&quot;../donate_button_paypal_01.gif&quot; border=&quot;0&quot; name=&quot;submit&quot; alt=&quot;Donate with PayPal&quot;&gt;
&lt;/form&gt;
&lt;/html&gt;
&lt;html&gt;
&lt;a href=&quot;http://me.alipay.com/agentzh&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;donate-with-alipay.png&quot;&gt;&lt;/a&gt;

&lt;a href=&quot;http://me.alipay.com/agentzh&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;../donate-with-alipay.png&quot;&gt;&lt;/a&gt;
&lt;/html&gt;
</pre>
</div>
<div title="MarkupPostBody" creator="YichunZhang" modifier="YichunZhang" created="201107251515" modified="201303070642" changecount="10">
<pre>&lt;p/&gt;&lt;p/&gt;
&lt;script type=&quot;text/javascript&quot;&gt;

  var _gaq = _gaq || [];
  _gaq.push(['_setAccount', 'UA-24724965-1']);
  _gaq.push(['_setDomainName', 'openresty.org']);
  _gaq.push(['_trackPageview']);

  var ga_f = function() {
    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
  };
  setTimeout(ga_f, 200);

&lt;/script&gt;</pre>
</div>
<div title="MarkupPostHead" creator="YichunZhang" modifier="YichunZhang" created="201107240444" modified="201107251514" changecount="4">
<pre></pre>
</div>
<div title="MemcNginxModule" creator="YichunZhang" modifier="YichunZhang" created="201106210830" modified="201108260111" changecount="3">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

This module extends the standard memcached module  to support almost the whole memcached ascii protocol.

It allows you to define a custom REST interface to your memcached servers or access memcached in a very efficient way from within the nginx server by means of subrequests or independent fake requests. 

Documentation: http://wiki.nginx.org/HttpMemcModule
Project page: http://github.com/agentzh/memc-nginx-module</pre>
</div>
<div title="Nginx" creator="YichunZhang" modifier="YichunZhang" created="201106210422" modified="201106210824" changecount="3">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

Nginx is a free, open-source, high-performance HTTP server and reverse proxy, as well as an IMAP/~POP3 proxy server. See its homepage: http://nginx.org.</pre>
</div>
<div title="NginxDevelKit" creator="YichunZhang" modifier="YichunZhang" created="201106210827" modified="201106210839" changecount="2">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

The NDK is an Nginx module that is designed to extend the core functionality of the    excellent Nginx webserver in a way that can be used as a basis of other Nginx modules.

Project page: https://github.com/simpl/ngx_devel_kit</pre>
</div>
<div title="OpenResty" creator="YichunZhang" modifier="ZoomQuiet" created="201106210403" modified="201205291507" changecount="12">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

OpenResty （也称为 ngx_openresty）是一个全功能的 Web 应用服务器，它打包了标准的 [[Nginx]] 核心，很多的常用的[[第三方模块|http://wiki.nginx.org/3rdPartyModules]]，以及它们的大多数依赖项。

~OpenResty 通过汇聚各种设计精良的 Nginx 模块,
从而将 Nginx 有效的变成一个强大的 Web 应用服务器,
这样, Web 开发人员可以使用 Lua 脚本语言调动 Nginx 支持的各种C以及Lua 模块,
快速构造出足以胜任 10K+ 并发连接响应的超高性能Web 应用系统.

~OpenResty 的目标是让你的Web服务直接跑在 Nginx 服务内部,
充分利用 Nginx 的非阻塞 I/O 模型,
不仅仅对 HTTP 客户端请求,甚至于对远程后端诸如
~MySQL,~PostgreSQL,~Memcaches 以及 ~Redis 等都进行一致的高性能响应.


参考 [[组件|Components]] 可以知道 ~OpenResty 中包含了多少软件.


参考 [[上路|GettingStarted]] 学习如何从最简单的 hello world 开始使用 ~OpenResty 开发 HTTP 业务,
或前往  [[下载|Download]] 直接获取 ~OpenResty 的源代码包开始体验.
</pre>
</div>
<div title="PostgresNginxModule" creator="YichunZhang" modifier="YichunZhang" created="201106210828" modified="201106220736" changecount="3">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

ngx_postgres is an upstream module that allows [[Nginx]] to communicate directly with ~PostgreSQL database.

Response is generated in RDS format, so it's compatible with RdsJsonNginxModule and DrizzleNginxModule modules.

This module is not enabled by default, and you need to specify the {{{--with-http_postgres_module}}} option while [[building OpenResty|Installation]]. This Nginx module requires libpq to be installed into your system.

Project page: http://github.com/FRiCKLE/ngx_postgres
</pre>
</div>
<div title="Presentations" creator="YichunZhang" modifier="ZoomQuiet" created="201106241028" modified="201205291423" tags="Resources" changecount="6">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

Here goes a list of slides that I used in my talks regarding OpenResty.

These slides are powered by the [[slides.htm|https://github.com/kindy61/slides.htm]] slide-making engine based on AJAX.

''//Note: Please use the arrow keys or pageup/pagedown keys on your keyboard to switch pages.//''
! Year 2012
!! ngx_openresty: an Nginx ecosystem glued by Lua
This talk was given at [[Tech-Club Technical Salon|http://event.weibo.com/351359]] held in the Xiamen city.

View in your web browser: http://agentzh.org/misc/slides/ngx-openresty-ecosystem/

Download the slides as PDF: http://agentzh.org/misc/slides/ngx-openresty-ecosystem.pdf

!! Scripting libdrizzle with Lua inside Nginx
This presentation was given at [[Percona Live MySQL Conference 2012|http://www.percona.com/live/mysql-conference-2012/sessions/scripting-mysql-lua-and-libdrizzle-inside-nginx]] held in Santa Clara, CA, USA.

View in your web browser: http://agentzh.org/misc/slides/libdrizzle-lua-nginx/#2

Download the slides as PDF: http://agentzh.org/misc/slides/libdrizzle-lua-nginx.pdf

! Year 2011
!! Applications of ngx_openresty and perl in lz.taobao.com

This talk was given at [[Beijing Perl Workshop 2011|http://conference.perlchina.org/bjpw2011/talks]].

Watch the video online: http://v.ku6.com/show/TY8Vre59guTE_C8o.html

View in your web browser: http://agentzh.org/misc/slides/perl-lz-apps/

Download the slides as PDF: http://agentzh.org/misc/slides/perl-lz-apps.pdf

! Year 2010
!! Introduction to nginx.conf scripting
This talk was given at the [[Beijing Perl Workshop|http://conference.perlchina.org]] 2010 April meeting and the [[Beijing OpenParty 2010 June event|http://www.beijing-open-party.org/event/2]].

View in your web browser: http://agentzh.org/misc/slides/nginx-conf-scripting/

Download the slides as PDF: http://agentzh.org/misc/slides/nginx-conf-scripting.pdf

Please note that ngx_eval module is no longer recommended because we're in more favor of ngx_lua nowadays.

!! Recent developments in nginx.conf scripting
This talk was given at the [[Beijing OpenParty 2010 June event|http://www.beijing-open-party.org/event/2]].

View in your web browser: http://agentzh.org/misc/slides/recent-dev-nginx-conf/

Download the slides as PDF: http://agentzh.org/misc/slides/recent-dev-nginx-conf.pdf

!! The state of the art of nginx.conf scripting
This talk was given at the [[ECUG 2010 event|http://agentzh.org/misc/slides/nginx-state-of-the-art/]].

Watch the (Chinese-speech) video online: http://v.ku6.com/show/D00rqtnRwKzJdIsB.html

View the (English) slides in your web browser: http://agentzh.org/misc/slides/nginx-state-of-the-art/

Download the slides as PDF: http://agentzh.org/misc/slides/nginx-state-of-the-art.pdf
</pre>
</div>
<div title="QualityAssurance" creator="ZoomQuiet" modifier="ZoomQuiet" created="201205291437" modified="201205291439" changecount="2">
<pre>&lt;&lt;toolbar permalink&gt;&gt;


我们在亚马逊EC2的集群中,运行有针对所有组件的回归测试（包含其他第三方Nginx模块）。
最新的测试报告可以随时到此查阅：

http://qa.openresty.org/
</pre>
</div>
<div title="RSSFeed" creator="YichunZhang" modifier="YichunZhang" created="201106210735" modified="201106210739" changecount="3">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

This site's RSS  feed is available at http://openresty.org/index.xml [img[feed-icon-28x28.png]].</pre>
</div>
<div title="RdsCsvNginxModule" creator="YichunZhang" modifier="YichunZhang" created="201108310742" changecount="1">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

This Nginx module implements an efficient output filter that converts ~Resty-DBD-Streams (RDS) generated by DrizzleNginxModule and PostgresNginxModule to ~Comma-Separated Values (CSV) format in a streaming fashion.

By default, the CSV format is in compliance with RFC 4180:

        http://tools.ietf.org/html/rfc4180

This module is enabled by default in OpenResty. You can specify the {{{--without-http_rds_csv_module}}} option to disable this module while running {{{./configure}}} to build OpenResty.

Project homepage: https://github.com/agentzh/rds-csv-nginx-module</pre>
</div>
<div title="RdsJsonNginxModule" creator="YichunZhang" modifier="YichunZhang" created="201106210839" modified="201106210840" changecount="2">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

This is an output filter module that formats Resty DBD Streams (RDS) generated by ngx_drizzle and others to JSON streams.

Project page: http://github.com/agentzh/rds-json-nginx-module
</pre>
</div>
<div title="Redis2NginxModule" creator="YichunZhang" modifier="YichunZhang" created="201106210847" modified="201109060834" changecount="7">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

This is an [[Nginx]] upstream module that makes nginx talk to a redis 2.x server in a non-blocking way. The full Redis 2.0 unified protocol has been implemented including the redis pipelining support.

This module returns the raw TCP response from the redis server.

Documentation wiki page: http://wiki.nginx.org/HttpRedis2Module

Project page: https://github.com/agentzh/redis2-nginx-module</pre>
</div>
<div title="RedisNginxModule" creator="YichunZhang" modifier="YichunZhang" created="201309300700" changecount="1">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

This is an [[Nginx]] upstream module that makes nginx talk to a redis 2.x server in a non-blocking way. It has a similar interface with the standard [[ngx_memcached module|http://wiki.nginx.org/HttpMemcachedModule]], but only Redis {{{GET}}} and {{{SELECT}}} commands are supported.

This module returns the decoded result from the Redis server.

This module is written by Sergey A. Osokin.

This module is enabled by default and can be disabled by passing the {{{--without-http_redis_module}}} option to the {{{./configure}}} script for OpenResty.

Documentation wiki page: http://wiki.nginx.org/HttpRedisModule

When used in conjunction with [[LuaNginxModule]], it is recommended to use [[LuaRestyRedisLibrary]] instead of this one, since it is more flexible and more memory-efficient.
</pre>
</div>
<div title="ReleaseNote" creator="YichunZhang" modifier="YichunZhang" created="201111030906" modified="201111030927" changecount="9">
<pre></pre>
</div>
<div title="Resources" creator="YichunZhang" modifier="YichunZhang" created="201110240551" modified="201212111953" tags="Resources" changecount="11">
<pre>&lt;&lt;toolbar permalink&gt;&gt;
! English Articles
* Pushing Nginx to its limit with Lua
: http://blog.cloudflare.com/pushing-nginx-to-its-limit-with-lua
* My beautiful dark twisted reverse-proxy LRU cache
: http://mikeferrier.com/2011/05/14/my-beautiful-dark-twisted-reverse-proxy-LRU-cache/
* Day 41 - Setting up ngx_openresty (WAS: testing Test::Nginx) 
: http://www.nginx-discovery.com/2011/03/day-41-setting-up-ngxopenresty-was.html
! Chinese Articles
* An introduction to OpenResty
: http://wdicc.com/intro-openresty/
* Nginx 3rd-Party Module Experiments Journal
: http://chenxiaoyu.org/2011/10/30/nginx-modules.html
* Recommending OpenResty - An Nginx Version with Fully Capable Addons
: http://wendal.net/338.html
* Constructing Efficient and Transparent Caching Mechanism with MemcNginxModule and SrcacheNginxModule
: http://www.codinglabs.org/html/nginx-memc-and-srcache.html</pre>
</div>
<div title="Samples" creator="YichunZhang" modifier="ZoomQuiet" created="201107270400" modified="201205291423" tags="Resources" changecount="9">
<pre>&lt;&lt;toolbar permalink&gt;&gt;
* [[Dynamic request routing based on redis|DynamicRoutingBasedOnRedis]]
* [[Using LuaRocks|UsingLuaRocks]]
</pre>
</div>
<div title="SetMiscNginxModule" creator="YichunZhang" modifier="YichunZhang" created="201106210836" modified="201108260110" changecount="8">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

This module adds various {{{set_xxx}}} directives added to [[Nginx]]'s [[rewrite module|http://wiki.nginx.org/NginxHttpRewriteModule]] (~MD5/~SHA1, SQL/JSON quoting, and many more).

Every directive provided by this module can be mixed freely with other nginx rewrite module's
  directives, like {{{if}}} and {{{set}}}.

Documentation: http://wiki.nginx.org/HttpSetMiscModule

Project page: http://github.com/agentzh/set-misc-nginx-module</pre>
</div>
<div title="SiteSubtitle" creator="YichunZhang" modifier="YichunZhang" created="201106201138" modified="201111080413" tags="admin" changecount="3">
<pre>一个通过扩展 Nginx 实现的强大的 web 应用服务器</pre>
</div>
<div title="SiteTitle" creator="YichunZhang" modifier="YichunZhang" created="201106201137" modified="201106210357" tags="admin" changecount="3">
<pre>OpenResty</pre>
</div>
<div title="SrcacheNginxModule" creator="YichunZhang" modifier="YichunZhang" created="201106210907" modified="201108260111" changecount="5">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

This module provides a transparent caching layer for arbitrary nginx locations (like those use an upstream or even serve static disk files).

Usually, the MemcNginxModule is used together with this module to provide a concrete caching storage backend. But technically, any modules that provide a REST interface can be used as the fetching and storage subrequests used by this module.

Documentation page: http://wiki.nginx.org/HttpSRCacheModule

Project page: http://github.com/agentzh/srcache-nginx-module
</pre>
</div>
<div title="StandardLuaInterpreter" creator="YichunZhang" modifier="YichunZhang" created="201106210831" modified="201106210839" changecount="2">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

The standard Lua interpreter is enabled by default. If [[LuaJIT]] is enabled, this interpreter will be disabled automatically.

See Lua's homepage: http://www.lua.org/</pre>
</div>
<div title="StyleSheet" creator="YichunZhang" modifier="YichunZhang" created="201301230446" modified="201301230449" changecount="2">
<pre>body {
    font-size: 1.2em;
}</pre>
</div>
<div title="StyleSheetLayout" creator="YichunZhang" modifier="YichunZhang" created="201106220702" modified="201106220704" changecount="3">
<pre>/*{{{*/
* html .tiddler {height:1%;}

body {font-size:.75em; font-family:arial,helvetica; margin:0; padding:0;}

h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
h1,h2,h3 {padding-bottom:1px; margin-top:1.2em;margin-bottom:0.3em;}
h4,h5,h6 {margin-top:1em;}
h1 {font-size:1.35em;}
h2 {font-size:1.25em;}
h3 {font-size:1.1em;}
h4 {font-size:1em;}
h5 {font-size:.9em;}

hr {height:1px;}

a {text-decoration:none;}

dt {font-weight:bold;}

ol {list-style-type:decimal;}
ol ol {list-style-type:lower-alpha;}
ol ol ol {list-style-type:lower-roman;}
ol ol ol ol {list-style-type:decimal;}
ol ol ol ol ol {list-style-type:lower-alpha;}
ol ol ol ol ol ol {list-style-type:lower-roman;}
ol ol ol ol ol ol ol {list-style-type:decimal;}

.txtOptionInput {width:11em;}

#contentWrapper .chkOptionInput {border:0;}

.externalLink {text-decoration:underline;}

.indent {margin-left:3em;}
.outdent {margin-left:3em; text-indent:-3em;}
code.escaped {white-space:nowrap;}

.tiddlyLinkExisting {font-weight:bold;}
.tiddlyLinkNonExisting {font-style:italic;}

/* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
a.tiddlyLinkNonExisting.shadow {font-weight:bold;}

#mainMenu .tiddlyLinkExisting,
	#mainMenu .tiddlyLinkNonExisting,
	#sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
#sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}

.header {position:relative;}
.header a:hover {background:transparent;}
.headerShadow {position:relative; padding:2.5em 0 1em 1em; left:-1px; top:-1px;}
.headerForeground {position:absolute; padding:2.5em 0 1em 1em; left:0px; top:0px;}

.siteTitle {font-size:3em;}
.siteSubtitle {font-size:1.2em;}

#mainMenu {position:absolute; left:0; width:10em; text-align:right; line-height:1.6em; padding:1.5em 0.5em 0.5em 0.5em; font-size:1.1em;}

#sidebar {position:absolute; right:3px; width:16em; font-size:.9em;}
#sidebarOptions {padding-top:0.3em;}
#sidebarOptions a {margin:0 0.2em; padding:0.2em 0.3em; display:block;}
#sidebarOptions input {margin:0.4em 0.5em;}
#sidebarOptions .sliderPanel {margin-left:1em; padding:0.5em; font-size:.85em;}
#sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:0;}
#sidebarOptions .sliderPanel input {margin:0 0 0.3em 0;}
#sidebarTabs .tabContents {width:15em; overflow:hidden;}

.wizard {padding:0.1em 1em 0 2em;}
.wizard h1 {font-size:2em; font-weight:bold; background:none; padding:0; margin:0.4em 0 0.2em;}
.wizard h2 {font-size:1.2em; font-weight:bold; background:none; padding:0; margin:0.4em 0 0.2em;}
.wizardStep {padding:1em 1em 1em 1em;}
.wizard .button {margin:0.5em 0 0; font-size:1.2em;}
.wizardFooter {padding:0.8em 0.4em 0.8em 0;}
.wizardFooter .status {padding:0 0.4em; margin-left:1em;}
.wizard .button {padding:0.1em 0.2em;}

#messageArea {position:fixed; top:2em; right:0; margin:0.5em; padding:0.5em; z-index:2000; _position:absolute;}
.messageToolbar {display:block; text-align:right; padding:0.2em;}
#messageArea a {text-decoration:underline;}

.tiddlerPopupButton {padding:0.2em;}
.popupTiddler {position: absolute; z-index:300; padding:1em; margin:0;}

.popup {position:absolute; z-index:300; font-size:.9em; padding:0; list-style:none; margin:0;}
.popup .popupMessage {padding:0.4em;}
.popup hr {display:block; height:1px; width:auto; padding:0; margin:0.2em 0;}
.popup li.disabled {padding:0.4em;}
.popup li a {display:block; padding:0.4em; font-weight:normal; cursor:pointer;}
.listBreak {font-size:1px; line-height:1px;}
.listBreak div {margin:2px 0;}

.tabset {padding:1em 0 0 0.5em;}
.tab {margin:0 0 0 0.25em; padding:2px;}
.tabContents {padding:0.5em;}
.tabContents ul, .tabContents ol {margin:0; padding:0;}
.txtMainTab .tabContents li {list-style:none;}
.tabContents li.listLink { margin-left:.75em;}

#contentWrapper {display:block;}
#splashScreen {display:none;}

#displayArea {margin:1em 17em 0 14em;}

.toolbar {text-align:right; font-size:.9em;}

.tiddler {padding:1em 1em 0;}

.missing .viewer,.missing .title {font-style:italic;}

.title {font-size:1.6em; font-weight:bold;}

.missing .subtitle {display:none;}
.subtitle {font-size:1.1em;}

.tiddler .button {padding:0.2em 0.4em;}

.tagging {margin:0.5em 0.5em 0.5em 0; float:left; display:none;}
.isTag .tagging {display:block;}
.tagged {margin:0.5em; float:right;}
.tagging, .tagged {font-size:0.9em; padding:0.25em;}
.tagging ul, .tagged ul {list-style:none; margin:0.25em; padding:0;}
.tagClear {clear:both;}

.footer {font-size:.9em;}
.footer li {display:inline;}

.annotation {padding:0.5em; margin:0.5em;}

* html .viewer pre {width:99%; padding:0 0 1em 0;}
.viewer {line-height:1.4em; padding-top:0.5em;}
.viewer .button {margin:0 0.25em; padding:0 0.25em;}
.viewer blockquote {line-height:1.5em; padding-left:0.8em;margin-left:2.5em;}
.viewer ul, .viewer ol {margin-left:0.5em; padding-left:1.5em;}

.viewer table, table.twtable {border-collapse:collapse; margin:0.8em 1.0em;}
.viewer th, .viewer td, .viewer tr,.viewer caption,.twtable th, .twtable td, .twtable tr,.twtable caption {padding:3px;}
table.listView {font-size:0.85em; margin:0.8em 1.0em;}
table.listView th, table.listView td, table.listView tr {padding:0px 3px 0px 3px;}

.viewer pre {padding:0.5em; margin-left:0.5em; font-size:1.2em; line-height:1.4em; overflow:auto;}
.viewer code {font-size:1.2em; line-height:1.4em;}

.editor {font-size:1.1em;}
.editor input, .editor textarea {display:block; width:100%; font:inherit;}
.editorFooter {padding:0.25em 0; font-size:.9em;}
.editorFooter .button {padding-top:0px; padding-bottom:0px;}

.fieldsetFix {border:0; padding:0; margin:1px 0px;}

.sparkline {line-height:1em;}
.sparktick {outline:0;}

.zoomer {font-size:1.1em; position:absolute; overflow:hidden;}
.zoomer div {padding:1em;}

* html #backstage {width:99%;}
* html #backstageArea {width:99%;}
#backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em;}
#backstageToolbar {position:relative;}
#backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em;}
#backstageButton {display:none; position:absolute; z-index:175; top:0; right:0;}
#backstageButton a {padding:0.1em 0.4em; margin:0.1em;}
#backstage {position:relative; width:100%; z-index:50;}
#backstagePanel {display:none; z-index:100; position:absolute; width:90%; margin-left:3em; padding:1em;}
.backstagePanelFooter {padding-top:0.2em; float:right;}
.backstagePanelFooter a {padding:0.2em 0.4em;}
#backstageCloak {display:none; z-index:20; position:absolute; width:100%; height:100px;}

.whenBackstage {display:none;}
.backstageVisible .whenBackstage {display:block;}
/*}}}*/
</pre>
</div>
<div title="UpstreamKeepaliveNginxModule" creator="YichunZhang" modifier="YichunZhang" created="201106210908" modified="201108260111" changecount="3">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

This is a keepalive balancer module for nginx that implements cache for backend connections.

Documentation: http://wiki.nginx.org/HttpUpstreamKeepaliveModule

Project page: http://mdounin.ru/hg/ngx_http_upstream_keepalive/</pre>
</div>
<div title="UsingLuaRocks" creator="YichunZhang" modifier="YichunZhang" created="201108070232" modified="201111080431" changecount="44">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

使用 ~LuaRocks

这个示例中展示了在 OpenResty 中使用 [[LuaRocks|http://www.luarocks.org/]] 。这个示例已经在 Linux 和 Mac OS X 下，通过 Lua 解释器与 LuaJIT 的测试。

~LuaRocks 是一个部署和管理 Lua 模块的系统。

 ~LuaRocks 允许通过 &quot;rocks&quot; 安装独立的 Lua 模块,并且包含附加的版本信息。我们假设您已经在默认的路径里面安装了OpenResty.即, {{{/usr/local/openresty}}}. 您可以根据实际安装OpenResty的路径来调整本示例中的路径。如果您还没有安装 ~OpenResty, 请查看[[下载|Download]] 与[[安装|Installation]]页.

! 安装 ~LuaRocks
首先让我们来安装 ~LuaRocks:

请您从 http://www.luarocks.org/en/Download 下载最新版本的 ~LuaRocks . 当写这篇文章的时候,最新版本为 {{{2.0.4.1}}}（译注：目前最新版本已经更新到了2.0.5）, 在这个示例中我们将使用这个版本。（译注:如果您在安装了2.0.6版本的 ~LuaRocks，请记得填写正确的Lua解释器的地址,无论您使用 Lua 官方的解释器还是选择 LuaJIT，请确保安装时 ~LuaRocks 能找到它,否则 ~LuaRocks 可能报错）
{{{
wget http://luarocks.org/releases/luarocks-2.0.4.1.tar.gz
tar -xzvf luarocks-2.0.4.1.tar.gz
cd luarocks-2.0.4.1/
./configure
make
sudo make install
}}}
! 通过 ~LuaRocks安装 Lua ~MD5 库
在本示例中, 我们将使用 Lua ~MD5 library 作为服务器上的一个例子, 所以我们需要通过 ~LuaRocks 来安装它:
{{{
sudo luarocks install md5
}}}
! 配置我们的 ~OpenResty 应用
Let's change the current directory to {{{/usr/local/openresty/nginx/}}}:
{{{
cd /usr/local/openresty/nginx/
}}}
然后, 使用您喜欢的编辑器（例如 vim 或 emacs ）根据下面的内容，编辑 {{{conf/nginx.conf}}}  文件:
{{{
worker_processes  1;   # we could enlarge this setting on a multi-core machine
error_log  logs/error.log warn;

events {
    worker_connections  1024;
}

http {
    lua_package_path 'conf/?.lua;;';

    server {
        listen       80;
        server_name  localhost;

        location = /luarocks {
            content_by_lua '
                local foo = require(&quot;foo&quot;)
                foo.say(&quot;hello, luarocks!&quot;)
            ';
        }
    }
}
}}}
最后,我们创建下面两个 Lua 模块文件 {{{conf/foo.lua}}}
{{{
-- conf/foo.lua

module(&quot;foo&quot;, package.seeall)

local bar = require &quot;bar&quot;

ngx.say(&quot;bar loaded&quot;)

function say (var)
    bar.say(var)
end
}}}
和 {{{conf/bar.lua}}} 文件
{{{
-- conf/bar.lua

module(&quot;bar&quot;, package.seeall)

local rocks = require &quot;luarocks.loader&quot;
local md5 = require &quot;md5&quot;

ngx.say(&quot;rocks and md5 loaded&quot;)

function say (a)
    ngx.say(md5.sumhexa(a))
end
}}}
! 开启 Nginx 服务
现在我们通过 Nginx 开启我们的应用:
{{{
ulimit -n1024   # increase the maximal fd count limit per process
./sbin/nginx
}}}
如果您已经开启了 Nginx 服务,请先关闭后在重新开启:
{{{
./sbin/nginx -s stop  #（译注:我们也可以使用平滑重启命令完成此操作 ./sbin/nginx -s reload）
}}}
! 测试我们的应用
现在我们通过{{{curl}}} 工具或者任意兼容HTTP协议的浏览器测试我们的应用:
{{{
curl http://localhost/luarocks
}}}
我们在第一次运行的时候得到以下的内容:
{{{
rocks and md5 loaded
bar loaded
85e73df5c41378f830c031b81e4453d2
}}}
第二次运行的时候得到以下内容:
{{{
85e73df5c41378f830c031b81e4453d2
}}}
之所以会出现这样的输出数据是因为 LuaNginxModule 默认缓存了已经加载过的Lua模块 并且这些输出数据的代码是在 Lua 加载时运行的因此他们将不会在执行.

现在，让我们来做一些基准测试吧:
{{{
ab -c10 -n50000 http://127.0.0.1/luarocks
}}}
测试在是我的 Thinkpad T400 笔记本上进行的(~Core2Duo T9600 CPU), 下面是测试中产生的数据

{{{
Server Software:        ngx_openresty/1.0.4.2rc10
Server Hostname:        localhost
Server Port:            80

Document Path:          /luarocks
Document Length:        33 bytes

Concurrency Level:      10
Time taken for tests:   3.052 seconds
Complete requests:      50000
Failed requests:        0
Write errors:           0
Total transferred:      9400188 bytes
HTML transferred:       1650033 bytes
Requests per second:    16380.48 [#/sec] (mean)
Time per request:       0.610 [ms] (mean)
Time per request:       0.061 [ms] (mean, across all concurrent requests)
Transfer rate:          3007.41 [Kbytes/sec] received
}}}
注意哦上面测试的吞吐量是由单个 nginx 进程达到的。当您自己做这样的基准测试时，请注意在 {{{nginx.conf}}} 中设定的错误日志级别以及不要超过您本地机器运行的动态端口范围，否则的话测试的数据会显著变慢。

!已知问题
OpenResty 1.0.4.2rc10之前的版本, 将导致{{{lua_code_cache}}}  运行时在~LuaRocks 中的LuaNginxModule 模块抛出以下异常{{{error.log}}}:
{{{
lua handler aborted: runtime error: stack overflow
}}}
如果您正在使用的 OpenResty 版本是 1.0.4.2rc10 之前的版本, 请您考虑升级.
</pre>
</div>
<div title="XssNginxModule" creator="YichunZhang" modifier="YichunZhang" created="201106210905" modified="201106210907" changecount="2">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

This module adds cross-site AJAX support to nginx. Currently only cross-site GET is supported.

The cross-site GET is currently implemented as JSONP (or &quot;JSON with padding&quot;). See http://en.wikipedia.org/wiki/JSON#JSONP for more details.

Project page: http://github.com/agentzh/xss-nginx-module</pre>
</div>
<div title="YichunZhang" creator="YichunZhang" modifier="YichunZhang" created="201106210914" modified="201111080410" changecount="2">
<pre>章亦春是 OpenResty 软件包的主要作者之一。

个人首页: http://agentzh.org</pre>
</div>
<div title="eBooks" creator="ZoomQuiet" modifier="ZoomQuiet" created="201205291421" modified="201205291422" tags="Resources" changecount="2">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

! eBook &quot;agentzh's Nginx Tutorials&quot; (version 2012.04.18)
* English Edition
** [[HTML Single-Page Format|http://openresty.org/download/agentzh-nginx-tutorials-enuk.html]] (Web browser users)
** [[MOBI Format|http://openresty.org/download/agentzh-nginx-tutorials-enuk.mobi]] (Kindle users)
** [[EPUB Format|http://openresty.org/download/agentzh-nginx-tutorials-enuk.epub]] (Sony, Apple and other users)
** [[PDF Format|http://openresty.org/download/agentzh-nginx-tutorials-enuk.pdf]] (PC users)
* Chinese Edition
** [[HTML Single-Page Format|http://openresty.org/download/agentzh-nginx-tutorials-zhcn.html]] (Web browser users)
** [[MOBI Format|http://openresty.org/download/agentzh-nginx-tutorials-zhcn.mobi]] (Kindle users)
** [[EPUB Format|http://openresty.org/download/agentzh-nginx-tutorials-zhcn.epub]] (Sony, Apple and other users)
** [[PDF Format|http://openresty.org/download/agentzh-nginx-tutorials-zhcn.pdf]] (PC users)
* Git Repository
** https://github.com/agentzh/nginx-tutorials</pre>
</div>
<div title="libdrizzle" creator="YichunZhang" modifier="YichunZhang" created="201106210842" modified="201107010450" changecount="2">
<pre>&lt;&lt;toolbar permalink&gt;&gt;

This is the the client and protocol library for the Drizzle project. This library is now distributed with the Drizzle server release.

Project page: https://launchpad.net/drizzle
</pre>
</div>
</div>
<!--POST-STOREAREA-->
<!--POST-BODY-START-->
<!--POST-BODY-END-->
<script id="jsArea" type="text/javascript">
//<![CDATA[
//
// Please note:
//
// * This code is designed to be readable but for compactness it only includes brief comments. You can see fuller comments
//   in the project Subversion repository at http://svn.tiddlywiki.org/Trunk/core/
//
// * You should never need to modify this source code directly. TiddlyWiki is carefully designed to allow deep customisation
//   without changing the core code. Please consult the development group at http://groups.google.com/group/TiddlyWikiDev
//

//--
//-- Configuration repository
//--

// Miscellaneous options
var config = {
	numRssItems: 20, // Number of items in the RSS feed
	animDuration: 400, // Duration of UI animations in milliseconds
	cascadeFast: 20, // Speed for cascade animations (higher == slower)
	cascadeSlow: 60, // Speed for EasterEgg cascade animations
	cascadeDepth: 5, // Depth of cascade animation
	locale: "en" // W3C language tag
};

// Hashmap of alternative parsers for the wikifier
config.parsers = {};

// Adaptors
config.adaptors = {};
config.defaultAdaptor = null;

// Backstage tasks
config.tasks = {};

// Annotations
config.annotations = {};

// Custom fields to be automatically added to new tiddlers
config.defaultCustomFields = {};

// Messages
config.messages = {
	messageClose: {},
	dates: {},
	tiddlerPopup: {}
};

// Options that can be set in the options panel and/or cookies
config.options = {
	chkRegExpSearch: false,
	chkCaseSensitiveSearch: false,
	chkIncrementalSearch: true,
	chkAnimate: true,
	chkSaveBackups: true,
	chkAutoSave: false,
	chkGenerateAnRssFeed: false,
	chkSaveEmptyTemplate: false,
	chkOpenInNewWindow: true,
	chkToggleLinks: false,
	chkHttpReadOnly: true,
	chkForceMinorUpdate: false,
	chkConfirmDelete: true,
	chkInsertTabs: false,
	chkUsePreForStorage: true, // Whether to use <pre> format for storage
	chkDisplayInstrumentation: false,
	txtBackupFolder: "",
	txtEditorFocus: "text",
	txtMainTab: "tabTimeline",
	txtMoreTab: "moreTabAll",
	txtMaxEditRows: "30",
	txtFileSystemCharSet: "UTF-8",
	txtTheme: ""
	};
config.optionsDesc = {};

config.optionsSource = {};

// Default tiddler templates
var DEFAULT_VIEW_TEMPLATE = 1;
var DEFAULT_EDIT_TEMPLATE = 2;
config.tiddlerTemplates = {
	1: "ViewTemplate",
	2: "EditTemplate"
};

// More messages (rather a legacy layout that should not really be like this)
config.views = {
	wikified: {
		tag: {}
	},
	editor: {
		tagChooser: {}
	}
};

// Backstage tasks
config.backstageTasks = ["save","sync","importTask","tweak","upgrade","plugins"];

// Extensions
config.extensions = {};

// Macros; each has a 'handler' member that is inserted later
config.macros = {
	today: {},
	version: {},
	search: {sizeTextbox: 15},
	tiddler: {},
	tag: {},
	tags: {},
	tagging: {},
	timeline: {},
	allTags: {},
	list: {
		all: {},
		missing: {},
		orphans: {},
		shadowed: {},
		touched: {},
		filter: {}
	},
	closeAll: {},
	permaview: {},
	saveChanges: {},
	slider: {},
	option: {},
	options: {},
	newTiddler: {},
	newJournal: {},
	tabs: {},
	gradient: {},
	message: {},
	view: {defaultView: "text"},
	edit: {},
	tagChooser: {},
	toolbar: {},
	plugins: {},
	refreshDisplay: {},
	importTiddlers: {},
	upgrade: {
		source: "http://www.tiddlywiki.com/upgrade/",
		backupExtension: "pre.core.upgrade"
	},
	sync: {},
	annotations: {}
};

// Commands supported by the toolbar macro
config.commands = {
	closeTiddler: {},
	closeOthers: {},
	editTiddler: {},
	saveTiddler: {hideReadOnly: true},
	cancelTiddler: {},
	deleteTiddler: {hideReadOnly: true},
	permalink: {},
	references: {type: "popup"},
	jump: {type: "popup"},
	syncing: {type: "popup"},
	fields: {type: "popup"}
};

// Browser detection... In a very few places, there's nothing else for it but to know what browser we're using.
config.userAgent = navigator.userAgent.toLowerCase();
config.browser = {
	isIE: config.userAgent.indexOf("msie") != -1 && config.userAgent.indexOf("opera") == -1,
	isGecko: navigator.product == "Gecko" && config.userAgent.indexOf("WebKit") == -1,
	ieVersion: /MSIE (\d.\d)/i.exec(config.userAgent), // config.browser.ieVersion[1], if it exists, will be the IE version string, eg "6.0"
	isSafari: config.userAgent.indexOf("applewebkit") != -1,
	isBadSafari: !((new RegExp("[\u0150\u0170]","g")).test("\u0150")),
	firefoxDate: /gecko\/(\d{8})/i.exec(config.userAgent), // config.browser.firefoxDate[1], if it exists, will be Firefox release date as "YYYYMMDD"
	isOpera: config.userAgent.indexOf("opera") != -1,
	isLinux: config.userAgent.indexOf("linux") != -1,
	isUnix: config.userAgent.indexOf("x11") != -1,
	isMac: config.userAgent.indexOf("mac") != -1,
	isWindows: config.userAgent.indexOf("win") != -1
};

// Control of macro parameter evaluation
config.evaluateMacroParameters = "all";

// Basic regular expressions
config.textPrimitives = {
	upperLetter: "[A-Z\u00c0-\u00de\u0150\u0170]",
	lowerLetter: "[a-z0-9_\\-\u00df-\u00ff\u0151\u0171]",
	anyLetter:   "[A-Za-z0-9_\\-\u00c0-\u00de\u00df-\u00ff\u0150\u0170\u0151\u0171]",
	anyLetterStrict: "[A-Za-z0-9\u00c0-\u00de\u00df-\u00ff\u0150\u0170\u0151\u0171]"
};
if(config.browser.isBadSafari) {
	config.textPrimitives = {
		upperLetter: "[A-Z\u00c0-\u00de]",
		lowerLetter: "[a-z0-9_\\-\u00df-\u00ff]",
		anyLetter:   "[A-Za-z0-9_\\-\u00c0-\u00de\u00df-\u00ff]",
		anyLetterStrict: "[A-Za-z0-9\u00c0-\u00de\u00df-\u00ff]"
	};
}
config.textPrimitives.sliceSeparator = "::";
config.textPrimitives.sectionSeparator = "##";
config.textPrimitives.urlPattern = "(?:file|http|https|mailto|ftp|irc|news|data):[^\\s'\"]+(?:/|\\b)";
config.textPrimitives.unWikiLink = "~";
config.textPrimitives.wikiLink = "(?:(?:" + config.textPrimitives.upperLetter + "+" +
	config.textPrimitives.lowerLetter + "+" +
	config.textPrimitives.upperLetter +
	config.textPrimitives.anyLetter + "*)|(?:" +
	config.textPrimitives.upperLetter + "{2,}" +
	config.textPrimitives.lowerLetter + "+))";

config.textPrimitives.cssLookahead = "(?:(" + config.textPrimitives.anyLetter + "+)\\(([^\\)\\|\\n]+)(?:\\):))|(?:(" + config.textPrimitives.anyLetter + "+):([^;\\|\\n]+);)";
config.textPrimitives.cssLookaheadRegExp = new RegExp(config.textPrimitives.cssLookahead,"mg");

config.textPrimitives.brackettedLink = "\\[\\[([^\\]]+)\\]\\]";
config.textPrimitives.titledBrackettedLink = "\\[\\[([^\\[\\]\\|]+)\\|([^\\[\\]\\|]+)\\]\\]";
config.textPrimitives.tiddlerForcedLinkRegExp = new RegExp("(?:" + config.textPrimitives.titledBrackettedLink + ")|(?:" +
	config.textPrimitives.brackettedLink + ")|(?:" +
	config.textPrimitives.urlPattern + ")","mg");
config.textPrimitives.tiddlerAnyLinkRegExp = new RegExp("("+ config.textPrimitives.wikiLink + ")|(?:" +
	config.textPrimitives.titledBrackettedLink + ")|(?:" +
	config.textPrimitives.brackettedLink + ")|(?:" +
	config.textPrimitives.urlPattern + ")","mg");

config.glyphs = {
	browsers: [
		function() {return config.browser.isIE;},
		function() {return true;}
	],
	currBrowser: null,
	codes: {
		downTriangle: ["\u25BC","\u25BE"],
		downArrow: ["\u2193","\u2193"],
		bentArrowLeft: ["\u2190","\u21A9"],
		bentArrowRight: ["\u2192","\u21AA"]
	}
};

//--
//-- Shadow tiddlers
//--

config.shadowTiddlers = {
	StyleSheet: "",
	MarkupPreHead: "",
	MarkupPostHead: "",
	MarkupPreBody: "",
	MarkupPostBody: "",
	TabTimeline: '<<timeline>>',
	TabAll: '<<list all>>',
	TabTags: '<<allTags excludeLists>>',
	TabMoreMissing: '<<list missing>>',
	TabMoreOrphans: '<<list orphans>>',
	TabMoreShadowed: '<<list shadowed>>',
	AdvancedOptions: '<<options>>',
	PluginManager: '<<plugins>>',
	ToolbarCommands: '|~ViewToolbar|closeTiddler closeOthers +editTiddler > fields syncing permalink references jump|\n|~EditToolbar|+saveTiddler -cancelTiddler deleteTiddler|',
	WindowTitle: '<<tiddler SiteTitle>> - <<tiddler SiteSubtitle>>'
};

//--
//-- Translateable strings
//--

// Strings in "double quotes" should be translated; strings in 'single quotes' should be left alone

merge(config.options,{
	txtUserName: "YourName"});

merge(config.tasks,{
	save: {text: "save", tooltip: "Save your changes to this TiddlyWiki", action: saveChanges},
	sync: {text: "sync", tooltip: "Synchronise changes with other TiddlyWiki files and servers", content: '<<sync>>'},
	importTask: {text: "import", tooltip: "Import tiddlers and plugins from other TiddlyWiki files and servers", content: '<<importTiddlers>>'},
	tweak: {text: "tweak", tooltip: "Tweak the appearance and behaviour of TiddlyWiki", content: '<<options>>'},
	upgrade: {text: "upgrade", tooltip: "Upgrade TiddlyWiki core code", content: '<<upgrade>>'},
	plugins: {text: "plugins", tooltip: "Manage installed plugins", content: '<<plugins>>'}
});

// Options that can be set in the options panel and/or cookies
merge(config.optionsDesc,{
	txtUserName: "Username for signing your edits",
	chkRegExpSearch: "Enable regular expressions for searches",
	chkCaseSensitiveSearch: "Case-sensitive searching",
	chkIncrementalSearch: "Incremental key-by-key searching",
	chkAnimate: "Enable animations",
	chkSaveBackups: "Keep backup file when saving changes",
	chkAutoSave: "Automatically save changes",
	chkGenerateAnRssFeed: "Generate an RSS feed when saving changes",
	chkSaveEmptyTemplate: "Generate an empty template when saving changes",
	chkOpenInNewWindow: "Open external links in a new window",
	chkToggleLinks: "Clicking on links to open tiddlers causes them to close",
	chkHttpReadOnly: "Hide editing features when viewed over HTTP",
	chkForceMinorUpdate: "Don't update modifier username and date when editing tiddlers",
	chkConfirmDelete: "Require confirmation before deleting tiddlers",
	chkInsertTabs: "Use the tab key to insert tab characters instead of moving between fields",
	txtBackupFolder: "Name of folder to use for backups",
	txtMaxEditRows: "Maximum number of rows in edit boxes",
	txtTheme: "Name of the theme to use",
	txtFileSystemCharSet: "Default character set for saving changes (Firefox/Mozilla only)"});

merge(config.messages,{
	customConfigError: "Problems were encountered loading plugins. See PluginManager for details",
	pluginError: "Error: %0",
	pluginDisabled: "Not executed because disabled via 'systemConfigDisable' tag",
	pluginForced: "Executed because forced via 'systemConfigForce' tag",
	pluginVersionError: "Not executed because this plugin needs a newer version of TiddlyWiki",
	nothingSelected: "Nothing is selected. You must select one or more items first",
	savedSnapshotError: "It appears that this TiddlyWiki has been incorrectly saved. Please see http://www.tiddlywiki.com/#Download for details",
	subtitleUnknown: "(unknown)",
	undefinedTiddlerToolTip: "The tiddler '%0' doesn't yet exist",
	shadowedTiddlerToolTip: "The tiddler '%0' doesn't yet exist, but has a pre-defined shadow value",
	tiddlerLinkTooltip: "%0 - %1, %2",
	externalLinkTooltip: "External link to %0",
	noTags: "There are no tagged tiddlers",
	notFileUrlError: "You need to save this TiddlyWiki to a file before you can save changes",
	cantSaveError: "It's not possible to save changes. Possible reasons include:\n- your browser doesn't support saving (Firefox, Internet Explorer, Safari and Opera all work if properly configured)\n- the pathname to your TiddlyWiki file contains illegal characters\n- the TiddlyWiki HTML file has been moved or renamed",
	invalidFileError: "The original file '%0' does not appear to be a valid TiddlyWiki",
	backupSaved: "Backup saved",
	backupFailed: "Failed to save backup file",
	rssSaved: "RSS feed saved",
	rssFailed: "Failed to save RSS feed file",
	emptySaved: "Empty template saved",
	emptyFailed: "Failed to save empty template file",
	mainSaved: "Main TiddlyWiki file saved",
	mainFailed: "Failed to save main TiddlyWiki file. Your changes have not been saved",
	macroError: "Error in macro <<\%0>>",
	macroErrorDetails: "Error while executing macro <<\%0>>:\n%1",
	missingMacro: "No such macro",
	overwriteWarning: "A tiddler named '%0' already exists. Choose OK to overwrite it",
	unsavedChangesWarning: "WARNING! There are unsaved changes in TiddlyWiki\n\nChoose OK to save\nChoose CANCEL to discard",
	confirmExit: "--------------------------------\n\nThere are unsaved changes in TiddlyWiki. If you continue you will lose those changes\n\n--------------------------------",
	saveInstructions: "SaveChanges",
	unsupportedTWFormat: "Unsupported TiddlyWiki format '%0'",
	tiddlerSaveError: "Error when saving tiddler '%0'",
	tiddlerLoadError: "Error when loading tiddler '%0'",
	wrongSaveFormat: "Cannot save with storage format '%0'. Using standard format for save.",
	invalidFieldName: "Invalid field name %0",
	fieldCannotBeChanged: "Field '%0' cannot be changed",
	loadingMissingTiddler: "Attempting to retrieve the tiddler '%0' from the '%1' server at:\n\n'%2' in the workspace '%3'",
	upgradeDone: "The upgrade to version %0 is now complete\n\nClick 'OK' to reload the newly upgraded TiddlyWiki",
	invalidCookie: "Invalid cookie '%0'"});

merge(config.messages.messageClose,{
	text: "close",
	tooltip: "close this message area"});

config.messages.backstage = {
	open: {text: "backstage", tooltip: "Open the backstage area to perform authoring and editing tasks"},
	close: {text: "close", tooltip: "Close the backstage area"},
	prompt: "backstage: ",
	decal: {
		edit: {text: "edit", tooltip: "Edit the tiddler '%0'"}
	}
};

config.messages.listView = {
	tiddlerTooltip: "Click for the full text of this tiddler",
	previewUnavailable: "(preview not available)"
};

config.messages.dates.months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November","December"];
config.messages.dates.days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
config.messages.dates.shortMonths = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
config.messages.dates.shortDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
// suffixes for dates, eg "1st","2nd","3rd"..."30th","31st"
config.messages.dates.daySuffixes = ["st","nd","rd","th","th","th","th","th","th","th",
		"th","th","th","th","th","th","th","th","th","th",
		"st","nd","rd","th","th","th","th","th","th","th",
		"st"];
config.messages.dates.am = "am";
config.messages.dates.pm = "pm";

merge(config.messages.tiddlerPopup,{
	});

merge(config.views.wikified.tag,{
	labelNoTags: "no tags",
	labelTags: "tags: ",
	openTag: "Open tag '%0'",
	tooltip: "Show tiddlers tagged with '%0'",
	openAllText: "Open all",
	openAllTooltip: "Open all of these tiddlers",
	popupNone: "No other tiddlers tagged with '%0'"});

merge(config.views.wikified,{
	defaultText: "The tiddler '%0' doesn't yet exist. Double-click to create it",
	defaultModifier: "(missing)",
	shadowModifier: "(built-in shadow tiddler)",
	dateFormat: "DD MMM YYYY",
	createdPrompt: "created"});

merge(config.views.editor,{
	tagPrompt: "Type tags separated with spaces, [[use double square brackets]] if necessary, or add existing",
	defaultText: "Type the text for '%0'"});

merge(config.views.editor.tagChooser,{
	text: "tags",
	tooltip: "Choose existing tags to add to this tiddler",
	popupNone: "There are no tags defined",
	tagTooltip: "Add the tag '%0'"});

merge(config.messages,{
	sizeTemplates:
		[
		{unit: 1024*1024*1024, template: "%0\u00a0GB"},
		{unit: 1024*1024, template: "%0\u00a0MB"},
		{unit: 1024, template: "%0\u00a0KB"},
		{unit: 1, template: "%0\u00a0B"}
		]});

merge(config.macros.search,{
	label: "search",
	prompt: "Search this TiddlyWiki",
	accessKey: "F",
	successMsg: "%0 tiddlers found matching %1",
	failureMsg: "No tiddlers found matching %0"});

merge(config.macros.tagging,{
	label: "tagging: ",
	labelNotTag: "not tagging",
	tooltip: "List of tiddlers tagged with '%0'"});

merge(config.macros.timeline,{
	dateFormat: "DD MMM YYYY"});

merge(config.macros.allTags,{
	tooltip: "Show tiddlers tagged with '%0'",
	noTags: "There are no tagged tiddlers"});

config.macros.list.all.prompt = "All tiddlers in alphabetical order";
config.macros.list.missing.prompt = "Tiddlers that have links to them but are not defined";
config.macros.list.orphans.prompt = "Tiddlers that are not linked to from any other tiddlers";
config.macros.list.shadowed.prompt = "Tiddlers shadowed with default contents";
config.macros.list.touched.prompt = "Tiddlers that have been modified locally";

merge(config.macros.closeAll,{
	label: "close all",
	prompt: "Close all displayed tiddlers (except any that are being edited)"});

merge(config.macros.permaview,{
	label: "permaview",
	prompt: "Link to an URL that retrieves all the currently displayed tiddlers"});

merge(config.macros.saveChanges,{
	label: "save changes",
	prompt: "Save all tiddlers to create a new TiddlyWiki",
	accessKey: "S"});

merge(config.macros.newTiddler,{
	label: "new tiddler",
	prompt: "Create a new tiddler",
	title: "New Tiddler",
	accessKey: "N"});

merge(config.macros.newJournal,{
	label: "new journal",
	prompt: "Create a new tiddler from the current date and time",
	accessKey: "J"});

merge(config.macros.options,{
	wizardTitle: "Tweak advanced options",
	step1Title: "These options are saved in cookies in your browser",
	step1Html: "<input type='hidden' name='markList'></input><br><input type='checkbox' checked='false' name='chkUnknown'>Show unknown options</input>",
	unknownDescription: "//(unknown)//",
	listViewTemplate: {
		columns: [
			{name: 'Option', field: 'option', title: "Option", type: 'String'},
			{name: 'Description', field: 'description', title: "Description", type: 'WikiText'},
			{name: 'Name', field: 'name', title: "Name", type: 'String'}
			],
		rowClasses: [
			{className: 'lowlight', field: 'lowlight'}
			]}
	});

merge(config.macros.plugins,{
	wizardTitle: "Manage plugins",
	step1Title: "Currently loaded plugins",
	step1Html: "<input type='hidden' name='markList'></input>", // DO NOT TRANSLATE
	skippedText: "(This plugin has not been executed because it was added since startup)",
	noPluginText: "There are no plugins installed",
	confirmDeleteText: "Are you sure you want to delete these plugins:\n\n%0",
	removeLabel: "remove systemConfig tag",
	removePrompt: "Remove systemConfig tag",
	deleteLabel: "delete",
	deletePrompt: "Delete these tiddlers forever",
	listViewTemplate: {
		columns: [
			{name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
			{name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
			{name: 'Description', field: 'Description', title: "Description", type: 'String'},
			{name: 'Version', field: 'Version', title: "Version", type: 'String'},
			{name: 'Size', field: 'size', tiddlerLink: 'size', title: "Size", type: 'Size'},
			{name: 'Forced', field: 'forced', title: "Forced", tag: 'systemConfigForce', type: 'TagCheckbox'},
			{name: 'Disabled', field: 'disabled', title: "Disabled", tag: 'systemConfigDisable', type: 'TagCheckbox'},
			{name: 'Executed', field: 'executed', title: "Loaded", type: 'Boolean', trueText: "Yes", falseText: "No"},
			{name: 'Startup Time', field: 'startupTime', title: "Startup Time", type: 'String'},
			{name: 'Error', field: 'error', title: "Status", type: 'Boolean', trueText: "Error", falseText: "OK"},
			{name: 'Log', field: 'log', title: "Log", type: 'StringList'}
			],
		rowClasses: [
			{className: 'error', field: 'error'},
			{className: 'warning', field: 'warning'}
			]},
	listViewTemplateReadOnly: {
		columns: [
			{name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
			{name: 'Description', field: 'Description', title: "Description", type: 'String'},
			{name: 'Version', field: 'Version', title: "Version", type: 'String'},
			{name: 'Size', field: 'size', tiddlerLink: 'size', title: "Size", type: 'Size'},
			{name: 'Executed', field: 'executed', title: "Loaded", type: 'Boolean', trueText: "Yes", falseText: "No"},
			{name: 'Startup Time', field: 'startupTime', title: "Startup Time", type: 'String'},
			{name: 'Error', field: 'error', title: "Status", type: 'Boolean', trueText: "Error", falseText: "OK"},
			{name: 'Log', field: 'log', title: "Log", type: 'StringList'}
			],
		rowClasses: [
			{className: 'error', field: 'error'},
			{className: 'warning', field: 'warning'}
			]}
	});

merge(config.macros.toolbar,{
	moreLabel: "more",
	morePrompt: "Show additional commands",
	lessLabel: "less",
	lessPrompt: "Hide additional commands",
	separator: "|"
	});

merge(config.macros.refreshDisplay,{
	label: "refresh",
	prompt: "Redraw the entire TiddlyWiki display"
	});

merge(config.macros.importTiddlers,{
	readOnlyWarning: "You cannot import into a read-only TiddlyWiki file. Try opening it from a file:// URL",
	wizardTitle: "Import tiddlers from another file or server",
	step1Title: "Step 1: Locate the server or TiddlyWiki file",
	step1Html: "Specify the type of the server: <select name='selTypes'><option value=''>Choose...</option></select><br>Enter the URL or pathname here: <input type='text' size=50 name='txtPath'><br>...or browse for a file: <input type='file' size=50 name='txtBrowse'><br><hr>...or select a pre-defined feed: <select name='selFeeds'><option value=''>Choose...</option></select>",
	openLabel: "open",
	openPrompt: "Open the connection to this file or server",
	statusOpenHost: "Opening the host",
	statusGetWorkspaceList: "Getting the list of available workspaces",
	step2Title: "Step 2: Choose the workspace",
	step2Html: "Enter a workspace name: <input type='text' size=50 name='txtWorkspace'><br>...or select a workspace: <select name='selWorkspace'><option value=''>Choose...</option></select>",
	cancelLabel: "cancel",
	cancelPrompt: "Cancel this import",
	statusOpenWorkspace: "Opening the workspace",
	statusGetTiddlerList: "Getting the list of available tiddlers",
	errorGettingTiddlerList: "Error getting list of tiddlers, click Cancel to try again",
	step3Title: "Step 3: Choose the tiddlers to import",
	step3Html: "<input type='hidden' name='markList'></input><br><input type='checkbox' checked='true' name='chkSync'>Keep these tiddlers linked to this server so that you can synchronise subsequent changes</input><br><input type='checkbox' name='chkSave'>Save the details of this server in a 'systemServer' tiddler called:</input> <input type='text' size=25 name='txtSaveTiddler'>",
	importLabel: "import",
	importPrompt: "Import these tiddlers",
	confirmOverwriteText: "Are you sure you want to overwrite these tiddlers:\n\n%0",
	step4Title: "Step 4: Importing %0 tiddler(s)",
	step4Html: "<input type='hidden' name='markReport'></input>", // DO NOT TRANSLATE
	doneLabel: "done",
	donePrompt: "Close this wizard",
	statusDoingImport: "Importing tiddlers",
	statusDoneImport: "All tiddlers imported",
	systemServerNamePattern: "%2 on %1",
	systemServerNamePatternNoWorkspace: "%1",
	confirmOverwriteSaveTiddler: "The tiddler '%0' already exists. Click 'OK' to overwrite it with the details of this server, or 'Cancel' to leave it unchanged",
	serverSaveTemplate: "|''Type:''|%0|\n|''URL:''|%1|\n|''Workspace:''|%2|\n\nThis tiddler was automatically created to record the details of this server",
	serverSaveModifier: "(System)",
	listViewTemplate: {
		columns: [
			{name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
			{name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
			{name: 'Size', field: 'size', tiddlerLink: 'size', title: "Size", type: 'Size'},
			{name: 'Tags', field: 'tags', title: "Tags", type: 'Tags'}
			],
		rowClasses: [
			]}
	});

merge(config.macros.upgrade,{
	wizardTitle: "Upgrade TiddlyWiki core code",
	step1Title: "Update or repair this TiddlyWiki to the latest release",
	step1Html: "You are about to upgrade to the latest release of the TiddlyWiki core code (from <a href='%0' class='externalLink' target='_blank'>%1</a>). Your content will be preserved across the upgrade.<br><br>Note that core upgrades have been known to interfere with older plugins. If you run into problems with the upgraded file, see <a href='http://www.tiddlywiki.org/wiki/CoreUpgrades' class='externalLink' target='_blank'>http://www.tiddlywiki.org/wiki/CoreUpgrades</a>",
	errorCantUpgrade: "Unable to upgrade this TiddlyWiki. You can only perform upgrades on TiddlyWiki files stored locally",
	errorNotSaved: "You must save changes before you can perform an upgrade",
	step2Title: "Confirm the upgrade details",
	step2Html_downgrade: "You are about to downgrade to TiddlyWiki version %0 from %1.<br><br>Downgrading to an earlier version of the core code is not recommended",
	step2Html_restore: "This TiddlyWiki appears to be already using the latest version of the core code (%0).<br><br>You can continue to upgrade anyway to ensure that the core code hasn't been corrupted or damaged",
	step2Html_upgrade: "You are about to upgrade to TiddlyWiki version %0 from %1",
	upgradeLabel: "upgrade",
	upgradePrompt: "Prepare for the upgrade process",
	statusPreparingBackup: "Preparing backup",
	statusSavingBackup: "Saving backup file",
	errorSavingBackup: "There was a problem saving the backup file",
	statusLoadingCore: "Loading core code",
	errorLoadingCore: "Error loading the core code",
	errorCoreFormat: "Error with the new core code",
	statusSavingCore: "Saving the new core code",
	statusReloadingCore: "Reloading the new core code",
	startLabel: "start",
	startPrompt: "Start the upgrade process",
	cancelLabel: "cancel",
	cancelPrompt: "Cancel the upgrade process",
	step3Title: "Upgrade cancelled",
	step3Html: "You have cancelled the upgrade process"
	});

merge(config.macros.sync,{
	listViewTemplate: {
		columns: [
			{name: 'Selected', field: 'selected', rowName: 'title', type: 'Selector'},
			{name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
			{name: 'Server Type', field: 'serverType', title: "Server type", type: 'String'},
			{name: 'Server Host', field: 'serverHost', title: "Server host", type: 'String'},
			{name: 'Server Workspace', field: 'serverWorkspace', title: "Server workspace", type: 'String'},
			{name: 'Status', field: 'status', title: "Synchronisation status", type: 'String'},
			{name: 'Server URL', field: 'serverUrl', title: "Server URL", text: "View", type: 'Link'}
			],
		rowClasses: [
			],
		buttons: [
			{caption: "Sync these tiddlers", name: 'sync'}
			]},
	wizardTitle: "Synchronize with external servers and files",
	step1Title: "Choose the tiddlers you want to synchronize",
	step1Html: "<input type='hidden' name='markList'></input>", // DO NOT TRANSLATE
	syncLabel: "sync",
	syncPrompt: "Sync these tiddlers",
	hasChanged: "Changed while unplugged",
	hasNotChanged: "Unchanged while unplugged",
	syncStatusList: {
		none: {text: "...", display:'none', className:'notChanged'},
		changedServer: {text: "Changed on server", display:null, className:'changedServer'},
		changedLocally: {text: "Changed while unplugged", display:null, className:'changedLocally'},
		changedBoth: {text: "Changed while unplugged and on server", display:null, className:'changedBoth'},
		notFound: {text: "Not found on server", display:null, className:'notFound'},
		putToServer: {text: "Saved update on server", display:null, className:'putToServer'},
		gotFromServer: {text: "Retrieved update from server", display:null, className:'gotFromServer'}
		}
	});

merge(config.macros.annotations,{
	});

merge(config.commands.closeTiddler,{
	text: "close",
	tooltip: "Close this tiddler"});

merge(config.commands.closeOthers,{
	text: "close others",
	tooltip: "Close all other tiddlers"});

merge(config.commands.editTiddler,{
	text: "edit",
	tooltip: "Edit this tiddler",
	readOnlyText: "view",
	readOnlyTooltip: "View the source of this tiddler"});

merge(config.commands.saveTiddler,{
	text: "done",
	tooltip: "Save changes to this tiddler"});

merge(config.commands.cancelTiddler,{
	text: "cancel",
	tooltip: "Undo changes to this tiddler",
	warning: "Are you sure you want to abandon your changes to '%0'?",
	readOnlyText: "done",
	readOnlyTooltip: "View this tiddler normally"});

merge(config.commands.deleteTiddler,{
	text: "delete",
	tooltip: "Delete this tiddler",
	warning: "Are you sure you want to delete '%0'?"});

merge(config.commands.permalink,{
	text: "permalink",
	tooltip: "Permalink for this tiddler"});

merge(config.commands.references,{
	text: "references",
	tooltip: "Show tiddlers that link to this one",
	popupNone: "No references"});

merge(config.commands.jump,{
	text: "jump",
	tooltip: "Jump to another open tiddler"});

merge(config.commands.syncing,{
	text: "syncing",
	tooltip: "Control synchronisation of this tiddler with a server or external file",
	currentlySyncing: "<div>Currently syncing via <span class='popupHighlight'>'%0'</span> to:</"+"div><div>host: <span class='popupHighlight'>%1</span></"+"div><div>workspace: <span class='popupHighlight'>%2</span></"+"div>", // Note escaping of closing <div> tag
	notCurrentlySyncing: "Not currently syncing",
	captionUnSync: "Stop synchronising this tiddler",
	chooseServer: "Synchronise this tiddler with another server:",
	currServerMarker: "\u25cf ",
	notCurrServerMarker: "  "});

merge(config.commands.fields,{
	text: "fields",
	tooltip: "Show the extended fields of this tiddler",
	emptyText: "There are no extended fields for this tiddler",
	listViewTemplate: {
		columns: [
			{name: 'Field', field: 'field', title: "Field", type: 'String'},
			{name: 'Value', field: 'value', title: "Value", type: 'String'}
			],
		rowClasses: [
			],
		buttons: [
			]}});

merge(config.shadowTiddlers,{
	DefaultTiddlers: "[[GettingStarted]]",
	MainMenu: "[[GettingStarted]]",
	SiteTitle: "My TiddlyWiki",
	SiteSubtitle: "a reusable non-linear personal web notebook",
	SiteUrl: "",
	SideBarOptions: '<<search>><<closeAll>><<permaview>><<newTiddler>><<newJournal "DD MMM YYYY" "journal">><<saveChanges>><<slider chkSliderOptionsPanel OptionsPanel "options \u00bb" "Change TiddlyWiki advanced options">>',
	SideBarTabs: '<<tabs txtMainTab "Timeline" "Timeline" TabTimeline "All" "All tiddlers" TabAll "Tags" "All tags" TabTags "More" "More lists" TabMore>>',
	TabMore: '<<tabs txtMoreTab "Missing" "Missing tiddlers" TabMoreMissing "Orphans" "Orphaned tiddlers" TabMoreOrphans "Shadowed" "Shadowed tiddlers" TabMoreShadowed>>'
	});

merge(config.annotations,{
	AdvancedOptions: "This shadow tiddler provides access to several advanced options",
	ColorPalette: "These values in this shadow tiddler determine the colour scheme of the ~TiddlyWiki user interface",
	DefaultTiddlers: "The tiddlers listed in this shadow tiddler will be automatically displayed when ~TiddlyWiki starts up",
	EditTemplate: "The HTML template in this shadow tiddler determines how tiddlers look while they are being edited",
	GettingStarted: "This shadow tiddler provides basic usage instructions",
	ImportTiddlers: "This shadow tiddler provides access to importing tiddlers",
	MainMenu: "This shadow tiddler is used as the contents of the main menu in the left-hand column of the screen",
	MarkupPreHead: "This tiddler is inserted at the top of the <head> section of the TiddlyWiki HTML file",
	MarkupPostHead: "This tiddler is inserted at the bottom of the <head> section of the TiddlyWiki HTML file",
	MarkupPreBody: "This tiddler is inserted at the top of the <body> section of the TiddlyWiki HTML file",
	MarkupPostBody: "This tiddler is inserted at the end of the <body> section of the TiddlyWiki HTML file immediately after the script block",
	OptionsPanel: "This shadow tiddler is used as the contents of the options panel slider in the right-hand sidebar",
	PageTemplate: "The HTML template in this shadow tiddler determines the overall ~TiddlyWiki layout",
	PluginManager: "This shadow tiddler provides access to the plugin manager",
	SideBarOptions: "This shadow tiddler is used as the contents of the option panel in the right-hand sidebar",
	SideBarTabs: "This shadow tiddler is used as the contents of the tabs panel in the right-hand sidebar",
	SiteSubtitle: "This shadow tiddler is used as the second part of the page title",
	SiteTitle: "This shadow tiddler is used as the first part of the page title",
	SiteUrl: "This shadow tiddler should be set to the full target URL for publication",
	StyleSheetColors: "This shadow tiddler contains CSS definitions related to the color of page elements. ''DO NOT EDIT THIS TIDDLER'', instead make your changes in the StyleSheet shadow tiddler",
	StyleSheet: "This tiddler can contain custom CSS definitions",
	StyleSheetLayout: "This shadow tiddler contains CSS definitions related to the layout of page elements. ''DO NOT EDIT THIS TIDDLER'', instead make your changes in the StyleSheet shadow tiddler",
	StyleSheetLocale: "This shadow tiddler contains CSS definitions related to the translation locale",
	StyleSheetPrint: "This shadow tiddler contains CSS definitions for printing",
	SystemSettings: "This tiddler is used to store configuration options for this TiddlyWiki document",
	TabAll: "This shadow tiddler contains the contents of the 'All' tab in the right-hand sidebar",
	TabMore: "This shadow tiddler contains the contents of the 'More' tab in the right-hand sidebar",
	TabMoreMissing: "This shadow tiddler contains the contents of the 'Missing' tab in the right-hand sidebar",
	TabMoreOrphans: "This shadow tiddler contains the contents of the 'Orphans' tab in the right-hand sidebar",
	TabMoreShadowed: "This shadow tiddler contains the contents of the 'Shadowed' tab in the right-hand sidebar",
	TabTags: "This shadow tiddler contains the contents of the 'Tags' tab in the right-hand sidebar",
	TabTimeline: "This shadow tiddler contains the contents of the 'Timeline' tab in the right-hand sidebar",
	ToolbarCommands: "This shadow tiddler determines which commands are shown in tiddler toolbars",
	ViewTemplate: "The HTML template in this shadow tiddler determines how tiddlers look"
	});
//--
//-- Main
//--

var params = null; // Command line parameters
var store = null; // TiddlyWiki storage
var story = null; // Main story
var formatter = null; // Default formatters for the wikifier
var anim = typeof Animator == "function" ? new Animator() : null; // Animation engine
var readOnly = false; // Whether we're in readonly mode
var highlightHack = null; // Embarrassing hack department...
var hadConfirmExit = false; // Don't warn more than once
var safeMode = false; // Disable all plugins and cookies
var showBackstage; // Whether to include the backstage area
var installedPlugins = []; // Information filled in when plugins are executed
var startingUp = false; // Whether we're in the process of starting up
var pluginInfo,tiddler; // Used to pass information to plugins in loadPlugins()

// Whether to use the JavaSaver applet
var useJavaSaver = (config.browser.isSafari || config.browser.isOpera) && (document.location.toString().substr(0,4) != "http");

// Starting up
function main()
{
	var t10,t9,t8,t7,t6,t5,t4,t3,t2,t1,t0 = new Date();
	startingUp = true;
	var doc = jQuery(document);
	jQuery.noConflict();
	window.onbeforeunload = function(e) {if(window.confirmExit) return confirmExit();};
	params = getParameters();
	if(params)
		params = params.parseParams("open",null,false);
	store = new TiddlyWiki();
	invokeParamifier(params,"oninit");
	story = new Story("tiddlerDisplay","tiddler");
	addEvent(document,"click",Popup.onDocumentClick);
	saveTest();
	for(var s=0; s<config.notifyTiddlers.length; s++)
		store.addNotification(config.notifyTiddlers[s].name,config.notifyTiddlers[s].notify);
	t1 = new Date();
	loadShadowTiddlers();
	doc.trigger("loadShadows");
	t2 = new Date();
	store.loadFromDiv("storeArea","store",true);
	doc.trigger("loadTiddlers");
	loadOptions();
	t3 = new Date();
	invokeParamifier(params,"onload");
	t4 = new Date();
	readOnly = (window.location.protocol == "file:") ? false : config.options.chkHttpReadOnly;
	var pluginProblem = loadPlugins("systemConfig");
	doc.trigger("loadPlugins");
	t5 = new Date();
	formatter = new Formatter(config.formatters);
	invokeParamifier(params,"onconfig");
	story.switchTheme(config.options.txtTheme);
	showBackstage = showBackstage !== undefined ? showBackstage : !readOnly;
	t6 = new Date();
	for(var m in config.macros) {
		if(config.macros[m].init)
			config.macros[m].init();
	}
	t7 = new Date();
	store.notifyAll();
	t8 = new Date();
	restart();
	refreshDisplay();
	t9 = new Date();
	if(pluginProblem) {
		story.displayTiddler(null,"PluginManager");
		displayMessage(config.messages.customConfigError);
	}
	if(showBackstage)
		backstage.init();
	t10 = new Date();
	if(config.options.chkDisplayInstrumentation) {
		displayMessage("LoadShadows " + (t2-t1) + " ms");
		displayMessage("LoadFromDiv " + (t3-t2) + " ms");
		displayMessage("LoadPlugins " + (t5-t4) + " ms");
		displayMessage("Macro init " + (t7-t6) + " ms");
		displayMessage("Notify " + (t8-t7) + " ms");
		displayMessage("Restart " + (t9-t8) + " ms");
		displayMessage("Total: " + (t10-t0) + " ms");
	}
	startingUp = false;
	doc.trigger("startup");
}

// Called on unload. All functions called conditionally since they themselves may have been unloaded.
function unload()
{
	if(window.checkUnsavedChanges)
		checkUnsavedChanges();
	if(window.scrubNodes)
		scrubNodes(document.body);
}

// Restarting
function restart()
{
	invokeParamifier(params,"onstart");
	if(story.isEmpty()) {
		story.displayDefaultTiddlers();
	}
	window.scrollTo(0,0);
}

function saveTest()
{
	var s = document.getElementById("saveTest");
	if(s.hasChildNodes())
		alert(config.messages.savedSnapshotError);
	s.appendChild(document.createTextNode("savetest"));
}

function loadShadowTiddlers()
{
	var shadows = new TiddlyWiki();
	shadows.loadFromDiv("shadowArea","shadows",true);
	shadows.forEachTiddler(function(title,tiddler){config.shadowTiddlers[title] = tiddler.text;});
}

function loadPlugins(tag)
{
	if(safeMode)
		return false;
	var tiddlers = store.getTaggedTiddlers(tag);
	tiddlers.sort(function(a,b) {return a.title < b.title ? -1 : (a.title == b.title ? 0 : 1);});
	var toLoad = [];
	var nLoaded = 0;
	var map = {};
	var nPlugins = tiddlers.length;
	installedPlugins = [];
	for(var i=0; i<nPlugins; i++) {
		var p = getPluginInfo(tiddlers[i]);
		installedPlugins[i] = p;
		var n = p.Name || p.title;
		if(n)
			map[n] = p;
		n = p.Source;
		if(n)
			map[n] = p;
	}
	var visit = function(p) {
		if(!p || p.done)
			return;
		p.done = 1;
		var reqs = p.Requires;
		if(reqs) {
			reqs = reqs.readBracketedList();
			for(var i=0; i<reqs.length; i++)
				visit(map[reqs[i]]);
		}
		toLoad.push(p);
	};
	for(i=0; i<nPlugins; i++)
		visit(installedPlugins[i]);
	for(i=0; i<toLoad.length; i++) {
		p = toLoad[i];
		pluginInfo = p;
		tiddler = p.tiddler;
		if(isPluginExecutable(p)) {
			if(isPluginEnabled(p)) {
				p.executed = true;
				var startTime = new Date();
				try {
					if(tiddler.text)
						window.eval(tiddler.text);
					nLoaded++;
				} catch(ex) {
					p.log.push(config.messages.pluginError.format([exceptionText(ex)]));
					p.error = true;
					if(!console.tiddlywiki) {
						console.log("error evaluating " + tiddler.title, ex);
					}
				}
				pluginInfo.startupTime = String((new Date()) - startTime) + "ms";
			} else {
				nPlugins--;
			}
		} else {
			p.warning = true;
		}
	}
	return nLoaded != nPlugins;
}

function getPluginInfo(tiddler)
{
	var p = store.getTiddlerSlices(tiddler.title,["Name","Description","Version","Requires","CoreVersion","Date","Source","Author","License","Browsers"]);
	p.tiddler = tiddler;
	p.title = tiddler.title;
	p.log = [];
	return p;
}

// Check that a particular plugin is valid for execution
function isPluginExecutable(plugin)
{
	if(plugin.tiddler.isTagged("systemConfigForce")) {
		plugin.log.push(config.messages.pluginForced);
		return true;
	}
	if(plugin["CoreVersion"]) {
		var coreVersion = plugin["CoreVersion"].split(".");
		var w = parseInt(coreVersion[0],10) - version.major;
		if(w == 0 && coreVersion[1])
			w = parseInt(coreVersion[1],10) - version.minor;
		if(w == 0 && coreVersion[2])
			w = parseInt(coreVersion[2],10) - version.revision;
		if(w > 0) {
			plugin.log.push(config.messages.pluginVersionError);
			return false;
		}
	}
	return true;
}

function isPluginEnabled(plugin)
{
	if(plugin.tiddler.isTagged("systemConfigDisable")) {
		plugin.log.push(config.messages.pluginDisabled);
		return false;
	}
	return true;
}

function invokeMacro(place,macro,params,wikifier,tiddler)
{
	try {
		var m = config.macros[macro];
		if(m && m.handler) {
			var tiddlerElem = story.findContainingTiddler(place);
			window.tiddler = tiddlerElem ? store.getTiddler(tiddlerElem.getAttribute("tiddler")) : null;
			window.place = place;
			var allowEval = true;
			if(config.evaluateMacroParameters=="system") {
				if(!tiddler || tiddler.tags.indexOf("systemAllowEval") == -1) {
					allowEval = false;
				}
			}
			m.handler(place,macro,m.noPreParse?null:params.readMacroParams(!allowEval),wikifier,params,tiddler);
		} else {
			createTiddlyError(place,config.messages.macroError.format([macro]),config.messages.macroErrorDetails.format([macro,config.messages.missingMacro]));
		}
	} catch(ex) {
		createTiddlyError(place,config.messages.macroError.format([macro]),config.messages.macroErrorDetails.format([macro,ex.toString()]));
	}
}

//--
//-- Paramifiers
//--

function getParameters()
{
	var p = null;
	if(window.location.hash) {
		p = decodeURIComponent(window.location.hash.substr(1));
		if(config.browser.firefoxDate != null && config.browser.firefoxDate[1] < "20051111")
			p = convertUTF8ToUnicode(p);
	}
	return p;
}

function invokeParamifier(params,handler)
{
	if(!params || params.length == undefined || params.length <= 1)
		return;
	for(var i=1; i<params.length; i++) {
		var p = config.paramifiers[params[i].name];
		if(p && p[handler] instanceof Function)
			p[handler](params[i].value);
		else {
			var h = config.optionHandlers[params[i].name.substr(0,3)];
			if(h && h.set instanceof Function)
				h.set(params[i].name,params[i].value);
		}
	}
}

config.paramifiers = {};

config.paramifiers.start = {
	oninit: function(v) {
		safeMode = v.toLowerCase() == "safe";
	}
};

config.paramifiers.open = {
	onstart: function(v) {
		if(!readOnly || store.tiddlerExists(v) || store.isShadowTiddler(v))
			story.displayTiddler("bottom",v,null,false,null);
	}
};

config.paramifiers.story = {
	onstart: function(v) {
		var list = store.getTiddlerText(v,"").parseParams("open",null,false);
		invokeParamifier(list,"onstart");
	}
};

config.paramifiers.search = {
	onstart: function(v) {
		story.search(v,false,false);
	}
};

config.paramifiers.searchRegExp = {
	onstart: function(v) {
		story.prototype.search(v,false,true);
	}
};

config.paramifiers.tag = {
	onstart: function(v) {
		story.displayTiddlers(null,store.filterTiddlers("[tag["+v+"]]"),null,false,null);
	}
};

config.paramifiers.newTiddler = {
	onstart: function(v) {
		if(!readOnly) {
			story.displayTiddler(null,v,DEFAULT_EDIT_TEMPLATE);
			story.focusTiddler(v,"text");
		}
	}
};

config.paramifiers.newJournal = {
	onstart: function(v) {
		if(!readOnly) {
			var now = new Date();
			var title = now.formatString(v.trim());
			story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE);
			story.focusTiddler(title,"text");
		}
	}
};

config.paramifiers.readOnly = {
	onconfig: function(v) {
		var p = v.toLowerCase();
		readOnly = p == "yes" ? true : (p == "no" ? false : readOnly);
	}
};

config.paramifiers.theme = {
	onconfig: function(v) {
		story.switchTheme(v);
	}
};

config.paramifiers.upgrade = {
	onstart: function(v) {
		upgradeFrom(v);
	}
};

config.paramifiers.recent= {
	onstart: function(v) {
		var titles=[];
		var tiddlers=store.getTiddlers("modified","excludeLists").reverse();
		for(var i=0; i<v && i<tiddlers.length; i++)
			titles.push(tiddlers[i].title);
		story.displayTiddlers(null,titles);
	}
};

config.paramifiers.filter = {
	onstart: function(v) {
		story.displayTiddlers(null,store.filterTiddlers(v),null,false);
	}
};

//--
//-- Formatter helpers
//--

function Formatter(formatters)
{
	this.formatters = [];
	var pattern = [];
	for(var n=0; n<formatters.length; n++) {
		pattern.push("(" + formatters[n].match + ")");
		this.formatters.push(formatters[n]);
	}
	this.formatterRegExp = new RegExp(pattern.join("|"),"mg");
}

config.formatterHelpers = {

	createElementAndWikify: function(w)
	{
		w.subWikifyTerm(createTiddlyElement(w.output,this.element),this.termRegExp);
	},

	inlineCssHelper: function(w)
	{
		var styles = [];
		config.textPrimitives.cssLookaheadRegExp.lastIndex = w.nextMatch;
		var lookaheadMatch = config.textPrimitives.cssLookaheadRegExp.exec(w.source);
		while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
			var s,v;
			if(lookaheadMatch[1]) {
				s = lookaheadMatch[1].unDash();
				v = lookaheadMatch[2];
			} else {
				s = lookaheadMatch[3].unDash();
				v = lookaheadMatch[4];
			}
			if(s=="bgcolor")
				s = "backgroundColor";
			if(s=="float")
				s = "cssFloat";
			styles.push({style: s, value: v});
			w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
			config.textPrimitives.cssLookaheadRegExp.lastIndex = w.nextMatch;
			lookaheadMatch = config.textPrimitives.cssLookaheadRegExp.exec(w.source);
		}
		return styles;
	},

	applyCssHelper: function(e,styles)
	{
		for(var t=0; t< styles.length; t++) {
			try {
				e.style[styles[t].style] = styles[t].value;
			} catch (ex) {
			}
		}
	},

	enclosedTextHelper: function(w)
	{
		this.lookaheadRegExp.lastIndex = w.matchStart;
		var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
		if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
			var text = lookaheadMatch[1];
			if(config.browser.isIE)
				text = text.replace(/\n/g,"\r");
			createTiddlyElement(w.output,this.element,null,null,text);
			w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
		}
	},

	isExternalLink: function(link)
	{
		if(store.tiddlerExists(link) || store.isShadowTiddler(link)) {
			return false;
		}
		var urlRegExp = new RegExp(config.textPrimitives.urlPattern,"mg");
		if(urlRegExp.exec(link)) {
			return true;
		}
		if(link.indexOf(".")!=-1 || link.indexOf("\\")!=-1 || link.indexOf("/")!=-1 || link.indexOf("#")!=-1) {
			return true;
		}
		return false;
	}

};

//--
//-- Standard formatters
//--

config.formatters = [
{
	name: "table",
	match: "^\\|(?:[^\\n]*)\\|(?:[fhck]?)$",
	lookaheadRegExp: /^\|([^\n]*)\|([fhck]?)$/mg,
	rowTermRegExp: /(\|(?:[fhck]?)$\n?)/mg,
	cellRegExp: /(?:\|([^\n\|]*)\|)|(\|[fhck]?$\n?)/mg,
	cellTermRegExp: /((?:\x20*)\|)/mg,
	rowTypes: {"c":"caption", "h":"thead", "":"tbody", "f":"tfoot"},
	handler: function(w)
	{
		var table = createTiddlyElement(w.output,"table",null,"twtable");
		var prevColumns = [];
		var currRowType = null;
		var rowContainer;
		var rowCount = 0;
		w.nextMatch = w.matchStart;
		this.lookaheadRegExp.lastIndex = w.nextMatch;
		var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
		while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
			var nextRowType = lookaheadMatch[2];
			if(nextRowType == "k") {
				table.className = lookaheadMatch[1];
				w.nextMatch += lookaheadMatch[0].length+1;
			} else {
				if(nextRowType != currRowType) {
					rowContainer = createTiddlyElement(table,this.rowTypes[nextRowType]);
					currRowType = nextRowType;
				}
				if(currRowType == "c") {
					// Caption
					w.nextMatch++;
					if(rowContainer != table.firstChild)
						table.insertBefore(rowContainer,table.firstChild);
					rowContainer.setAttribute("align",rowCount == 0?"top":"bottom");
					w.subWikifyTerm(rowContainer,this.rowTermRegExp);
				} else {
					var theRow = createTiddlyElement(rowContainer,"tr",null,(rowCount&1)?"oddRow":"evenRow");
					theRow.onmouseover = function() {addClass(this,"hoverRow");};
					theRow.onmouseout = function() {removeClass(this,"hoverRow");};
					this.rowHandler(w,theRow,prevColumns);
					rowCount++;
				}
			}
			this.lookaheadRegExp.lastIndex = w.nextMatch;
			lookaheadMatch = this.lookaheadRegExp.exec(w.source);
		}
	},
	rowHandler: function(w,e,prevColumns)
	{
		var col = 0;
		var colSpanCount = 1;
		var prevCell = null;
		this.cellRegExp.lastIndex = w.nextMatch;
		var cellMatch = this.cellRegExp.exec(w.source);
		while(cellMatch && cellMatch.index == w.nextMatch) {
			if(cellMatch[1] == "~") {
				// Rowspan
				var last = prevColumns[col];
				if(last) {
					last.rowSpanCount++;
					last.element.setAttribute("rowspan",last.rowSpanCount);
					last.element.setAttribute("rowSpan",last.rowSpanCount); // Needed for IE
					last.element.valign = "center";
					if(colSpanCount > 1) {
						last.element.setAttribute("colspan",colSpanCount);
						last.element.setAttribute("colSpan",colSpanCount); // Needed for IE
						colSpanCount = 1;
					}
				}
				w.nextMatch = this.cellRegExp.lastIndex-1;
			} else if(cellMatch[1] == ">") {
				// Colspan
				colSpanCount++;
				w.nextMatch = this.cellRegExp.lastIndex-1;
			} else if(cellMatch[2]) {
				// End of row
				if(prevCell && colSpanCount > 1) {
					prevCell.setAttribute("colspan",colSpanCount);
					prevCell.setAttribute("colSpan",colSpanCount); // Needed for IE
				}
				w.nextMatch = this.cellRegExp.lastIndex;
				break;
			} else {
				// Cell
				w.nextMatch++;
				var styles = config.formatterHelpers.inlineCssHelper(w);
				var spaceLeft = false;
				var chr = w.source.substr(w.nextMatch,1);
				while(chr == " ") {
					spaceLeft = true;
					w.nextMatch++;
					chr = w.source.substr(w.nextMatch,1);
				}
				var cell;
				if(chr == "!") {
					cell = createTiddlyElement(e,"th");
					w.nextMatch++;
				} else {
					cell = createTiddlyElement(e,"td");
				}
				prevCell = cell;
				prevColumns[col] = {rowSpanCount:1,element:cell};
				if(colSpanCount > 1) {
					cell.setAttribute("colspan",colSpanCount);
					cell.setAttribute("colSpan",colSpanCount); // Needed for IE
					colSpanCount = 1;
				}
				config.formatterHelpers.applyCssHelper(cell,styles);
				w.subWikifyTerm(cell,this.cellTermRegExp);
				if(w.matchText.substr(w.matchText.length-2,1) == " ") // spaceRight
					cell.align = spaceLeft ? "center" : "left";
				else if(spaceLeft)
					cell.align = "right";
				w.nextMatch--;
			}
			col++;
			this.cellRegExp.lastIndex = w.nextMatch;
			cellMatch = this.cellRegExp.exec(w.source);
		}
	}
},

{
	name: "heading",
	match: "^!{1,6}",
	termRegExp: /(\n)/mg,
	handler: function(w)
	{
		w.subWikifyTerm(createTiddlyElement(w.output,"h" + w.matchLength),this.termRegExp);
	}
},

{
	name: "list",
	match: "^(?:[\\*#;:]+)",
	lookaheadRegExp: /^(?:(?:(\*)|(#)|(;)|(:))+)/mg,
	termRegExp: /(\n)/mg,
	handler: function(w)
	{
		var stack = [w.output];
		var currLevel = 0, currType = null;
		var listLevel, listType, itemType, baseType;
		w.nextMatch = w.matchStart;
		this.lookaheadRegExp.lastIndex = w.nextMatch;
		var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
		while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
			if(lookaheadMatch[1]) {
				listType = "ul";
				itemType = "li";
			} else if(lookaheadMatch[2]) {
				listType = "ol";
				itemType = "li";
			} else if(lookaheadMatch[3]) {
				listType = "dl";
				itemType = "dt";
			} else if(lookaheadMatch[4]) {
				listType = "dl";
				itemType = "dd";
			}
			if(!baseType)
				baseType = listType;
			listLevel = lookaheadMatch[0].length;
			w.nextMatch += lookaheadMatch[0].length;
			var t;
			if(listLevel > currLevel) {
				for(t=currLevel; t<listLevel; t++) {
					var target = (currLevel == 0) ? stack[stack.length-1] : stack[stack.length-1].lastChild;
					stack.push(createTiddlyElement(target,listType));
				}
			} else if(listType!=baseType && listLevel==1) {
				w.nextMatch -= lookaheadMatch[0].length;
				return;
			} else if(listLevel < currLevel) {
				for(t=currLevel; t>listLevel; t--)
					stack.pop();
			} else if(listLevel == currLevel && listType != currType) {
				stack.pop();
				stack.push(createTiddlyElement(stack[stack.length-1].lastChild,listType));
			}
			currLevel = listLevel;
			currType = listType;
			var e = createTiddlyElement(stack[stack.length-1],itemType);
			w.subWikifyTerm(e,this.termRegExp);
			this.lookaheadRegExp.lastIndex = w.nextMatch;
			lookaheadMatch = this.lookaheadRegExp.exec(w.source);
		}
	}
},

{
	name: "quoteByBlock",
	match: "^<<<\\n",
	termRegExp: /(^<<<(\n|$))/mg,
	element: "blockquote",
	handler: config.formatterHelpers.createElementAndWikify
},

{
	name: "quoteByLine",
	match: "^>+",
	lookaheadRegExp: /^>+/mg,
	termRegExp: /(\n)/mg,
	element: "blockquote",
	handler: function(w)
	{
		var stack = [w.output];
		var currLevel = 0;
		var newLevel = w.matchLength;
		var t;
		do {
			if(newLevel > currLevel) {
				for(t=currLevel; t<newLevel; t++)
					stack.push(createTiddlyElement(stack[stack.length-1],this.element));
			} else if(newLevel < currLevel) {
				for(t=currLevel; t>newLevel; t--)
					stack.pop();
			}
			currLevel = newLevel;
			w.subWikifyTerm(stack[stack.length-1],this.termRegExp);
			createTiddlyElement(stack[stack.length-1],"br");
			this.lookaheadRegExp.lastIndex = w.nextMatch;
			var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
			var matched = lookaheadMatch && lookaheadMatch.index == w.nextMatch;
			if(matched) {
				newLevel = lookaheadMatch[0].length;
				w.nextMatch += lookaheadMatch[0].length;
			}
		} while(matched);
	}
},

{
	name: "rule",
	match: "^----+$\\n?|<hr ?/?>\\n?",
	handler: function(w)
	{
		createTiddlyElement(w.output,"hr");
	}
},

{
	name: "monospacedByLine",
	match: "^(?:/\\*\\{\\{\\{\\*/|\\{\\{\\{|//\\{\\{\\{|<!--\\{\\{\\{-->)\\n",
	element: "pre",
	handler: function(w)
	{
		switch(w.matchText) {
		case "/*{{{*/\n": // CSS
			this.lookaheadRegExp = /\/\*\{\{\{\*\/\n*((?:^[^\n]*\n)+?)(\n*^\f*\/\*\}\}\}\*\/$\n?)/mg;
			break;
		case "{{{\n": // monospaced block
			this.lookaheadRegExp = /^\{\{\{\n((?:^[^\n]*\n)+?)(^\f*\}\}\}$\n?)/mg;
			break;
		case "//{{{\n": // plugin
			this.lookaheadRegExp = /^\/\/\{\{\{\n\n*((?:^[^\n]*\n)+?)(\n*^\f*\/\/\}\}\}$\n?)/mg;
			break;
		case "<!--{{{-->\n": //template
			this.lookaheadRegExp = /<!--\{\{\{-->\n*((?:^[^\n]*\n)+?)(\n*^\f*<!--\}\}\}-->$\n?)/mg;
			break;
		default:
			break;
		}
		config.formatterHelpers.enclosedTextHelper.call(this,w);
	}
},

{
	name: "wikifyComment",
	match: "^(?:/\\*\\*\\*|<!---)\\n",
	handler: function(w)
	{
		var termRegExp = (w.matchText == "/***\n") ? (/(^\*\*\*\/\n)/mg) : (/(^--->\n)/mg);
		w.subWikifyTerm(w.output,termRegExp);
	}
},

{
	name: "macro",
	match: "<<",
	lookaheadRegExp: /<<([^>\s]+)(?:\s*)((?:[^>]|(?:>(?!>)))*)>>/mg,
	handler: function(w)
	{
		this.lookaheadRegExp.lastIndex = w.matchStart;
		var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
		if(lookaheadMatch && lookaheadMatch.index == w.matchStart && lookaheadMatch[1]) {
			w.nextMatch = this.lookaheadRegExp.lastIndex;
			invokeMacro(w.output,lookaheadMatch[1],lookaheadMatch[2],w,w.tiddler);
		}
	}
},

{
	name: "prettyLink",
	match: "\\[\\[",
	lookaheadRegExp: /\[\[(.*?)(?:\|(~)?(.*?))?\]\]/mg,
	handler: function(w)
	{
		this.lookaheadRegExp.lastIndex = w.matchStart;
		var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
		if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
			var e;
			var text = lookaheadMatch[1];
			if(lookaheadMatch[3]) {
				// Pretty bracketted link
				var link = lookaheadMatch[3];
				e = (!lookaheadMatch[2] && config.formatterHelpers.isExternalLink(link)) ?
						createExternalLink(w.output,link) : createTiddlyLink(w.output,link,false,null,w.isStatic,w.tiddler);
			} else {
				// Simple bracketted link
				e = createTiddlyLink(w.output,text,false,null,w.isStatic,w.tiddler);
			}
			createTiddlyText(e,text);
			w.nextMatch = this.lookaheadRegExp.lastIndex;
		}
	}
},

{
	name: "wikiLink",
	match: config.textPrimitives.unWikiLink+"?"+config.textPrimitives.wikiLink,
	handler: function(w)
	{
		if(w.matchText.substr(0,1) == config.textPrimitives.unWikiLink) {
			w.outputText(w.output,w.matchStart+1,w.nextMatch);
			return;
		}
		if(w.matchStart > 0) {
			var preRegExp = new RegExp(config.textPrimitives.anyLetterStrict,"mg");
			preRegExp.lastIndex = w.matchStart-1;
			var preMatch = preRegExp.exec(w.source);
			if(preMatch.index == w.matchStart-1) {
				w.outputText(w.output,w.matchStart,w.nextMatch);
				return;
			}
		}
		if(w.autoLinkWikiWords || store.isShadowTiddler(w.matchText)) {
			var link = createTiddlyLink(w.output,w.matchText,false,null,w.isStatic,w.tiddler);
			w.outputText(link,w.matchStart,w.nextMatch);
		} else {
			w.outputText(w.output,w.matchStart,w.nextMatch);
		}
	}
},

{
	name: "urlLink",
	match: config.textPrimitives.urlPattern,
	handler: function(w)
	{
		w.outputText(createExternalLink(w.output,w.matchText),w.matchStart,w.nextMatch);
	}
},

{
	name: "image",
	match: "\\[[<>]?[Ii][Mm][Gg]\\[",
	lookaheadRegExp: /\[([<]?)(>?)[Ii][Mm][Gg]\[(?:([^\|\]]+)\|)?([^\[\]\|]+)\](?:\[([^\]]*)\])?\]/mg,
	handler: function(w)
	{
		this.lookaheadRegExp.lastIndex = w.matchStart;
		var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
		if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
			var e = w.output;
			if(lookaheadMatch[5]) {
				var link = lookaheadMatch[5];
				e = config.formatterHelpers.isExternalLink(link) ? createExternalLink(w.output,link) : createTiddlyLink(w.output,link,false,null,w.isStatic,w.tiddler);
				addClass(e,"imageLink");
			}
			var img = createTiddlyElement(e,"img");
			if(lookaheadMatch[1])
				img.align = "left";
			else if(lookaheadMatch[2])
				img.align = "right";
			if(lookaheadMatch[3]) {
				img.title = lookaheadMatch[3];
				img.setAttribute("alt",lookaheadMatch[3]);
			}
			img.src = lookaheadMatch[4];
			w.nextMatch = this.lookaheadRegExp.lastIndex;
		}
	}
},

{
	name: "html",
	match: "<[Hh][Tt][Mm][Ll]>",
	lookaheadRegExp: /<[Hh][Tt][Mm][Ll]>((?:.|\n)*?)<\/[Hh][Tt][Mm][Ll]>/mg,
	handler: function(w)
	{
		this.lookaheadRegExp.lastIndex = w.matchStart;
		var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
		if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
			createTiddlyElement(w.output,"span").innerHTML = lookaheadMatch[1];
			w.nextMatch = this.lookaheadRegExp.lastIndex;
		}
	}
},

{
	name: "commentByBlock",
	match: "/%",
	lookaheadRegExp: /\/%((?:.|\n)*?)%\//mg,
	handler: function(w)
	{
		this.lookaheadRegExp.lastIndex = w.matchStart;
		var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
		if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
			w.nextMatch = this.lookaheadRegExp.lastIndex;
	}
},

{
	name: "characterFormat",
	match: "''|//|__|\\^\\^|~~|--(?!\\s|$)|\\{\\{\\{",
	handler: function(w)
	{
		switch(w.matchText) {
		case "''":
			w.subWikifyTerm(w.output.appendChild(document.createElement("strong")),/('')/mg);
			break;
		case "//":
			w.subWikifyTerm(createTiddlyElement(w.output,"em"),/(\/\/)/mg);
			break;
		case "__":
			w.subWikifyTerm(createTiddlyElement(w.output,"u"),/(__)/mg);
			break;
		case "^^":
			w.subWikifyTerm(createTiddlyElement(w.output,"sup"),/(\^\^)/mg);
			break;
		case "~~":
			w.subWikifyTerm(createTiddlyElement(w.output,"sub"),/(~~)/mg);
			break;
		case "--":
			w.subWikifyTerm(createTiddlyElement(w.output,"strike"),/(--)/mg);
			break;
		case "{{{":
			var lookaheadRegExp = /\{\{\{((?:.|\n)*?)\}\}\}/mg;
			lookaheadRegExp.lastIndex = w.matchStart;
			var lookaheadMatch = lookaheadRegExp.exec(w.source);
			if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
				createTiddlyElement(w.output,"code",null,null,lookaheadMatch[1]);
				w.nextMatch = lookaheadRegExp.lastIndex;
			}
			break;
		}
	}
},

{
	name: "customFormat",
	match: "@@|\\{\\{",
	handler: function(w)
	{
		switch(w.matchText) {
		case "@@":
			var e = createTiddlyElement(w.output,"span");
			var styles = config.formatterHelpers.inlineCssHelper(w);
			if(styles.length == 0)
				e.className = "marked";
			else
				config.formatterHelpers.applyCssHelper(e,styles);
			w.subWikifyTerm(e,/(@@)/mg);
			break;
		case "{{":
			var lookaheadRegExp = /\{\{[\s]*([\w]+[\s\w]*)[\s]*\{(\n?)/mg;
			lookaheadRegExp.lastIndex = w.matchStart;
			var lookaheadMatch = lookaheadRegExp.exec(w.source);
			if(lookaheadMatch) {
				w.nextMatch = lookaheadRegExp.lastIndex;
				e = createTiddlyElement(w.output,lookaheadMatch[2] == "\n" ? "div" : "span",null,lookaheadMatch[1]);
				w.subWikifyTerm(e,/(\}\}\})/mg);
			}
			break;
		}
	}
},

{
	name: "mdash",
	match: "--",
	handler: function(w)
	{
		createTiddlyElement(w.output,"span").innerHTML = "&mdash;";
	}
},

{
	name: "lineBreak",
	match: "\\n|<br ?/?>",
	handler: function(w)
	{
		createTiddlyElement(w.output,"br");
	}
},

{
	name: "rawText",
	match: "\"{3}|<nowiki>",
	lookaheadRegExp: /(?:\"{3}|<nowiki>)((?:.|\n)*?)(?:\"{3}|<\/nowiki>)/mg,
	handler: function(w)
	{
		this.lookaheadRegExp.lastIndex = w.matchStart;
		var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
		if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
			createTiddlyElement(w.output,"span",null,null,lookaheadMatch[1]);
			w.nextMatch = this.lookaheadRegExp.lastIndex;
		}
	}
},

{
	name: "htmlEntitiesEncoding",
	match: "(?:(?:&#?[a-zA-Z0-9]{2,8};|.)(?:&#?(?:x0*(?:3[0-6][0-9a-fA-F]|1D[c-fC-F][0-9a-fA-F]|20[d-fD-F][0-9a-fA-F]|FE2[0-9a-fA-F])|0*(?:76[89]|7[7-9][0-9]|8[0-7][0-9]|761[6-9]|76[2-7][0-9]|84[0-3][0-9]|844[0-7]|6505[6-9]|6506[0-9]|6507[0-1]));)+|&#?[a-zA-Z0-9]{2,8};)",
	handler: function(w)
	{
		createTiddlyElement(w.output,"span").innerHTML = w.matchText;
	}
}

];

//--
//-- Wikifier
//--

function getParser(tiddler,format)
{
	if(tiddler) {
		if(!format)
			format = tiddler.fields["wikiformat"];
		var i;
		if(format) {
			for(i in config.parsers) {
				if(format == config.parsers[i].format)
					return config.parsers[i];
			}
		} else {
			for(i in config.parsers) {
				if(tiddler.isTagged(config.parsers[i].formatTag))
					return config.parsers[i];
			}
		}
	}
	return formatter;
}

function wikify(source,output,highlightRegExp,tiddler)
{
	if(source) {
		var wikifier = new Wikifier(source,getParser(tiddler),highlightRegExp,tiddler);
		var t0 = new Date();
		wikifier.subWikify(output);
		if(tiddler && config.options.chkDisplayInstrumentation)
			displayMessage("wikify:" +tiddler.title+ " in " + (new Date()-t0) + " ms");
	}
}

function wikifyStatic(source,highlightRegExp,tiddler,format)
{
	var e = createTiddlyElement(document.body,"pre");
	e.style.display = "none";
	var html = "";
	if(source && source != "") {
		if(!tiddler)
			tiddler = new Tiddler("temp");
		var wikifier = new Wikifier(source,getParser(tiddler,format),highlightRegExp,tiddler);
		wikifier.isStatic = true;
		wikifier.subWikify(e);
		html = e.innerHTML;
		removeNode(e);
	}
	return html;
}

function wikifyPlain(title,theStore,limit)
{
	if(!theStore)
		theStore = store;
	if(theStore.tiddlerExists(title) || theStore.isShadowTiddler(title)) {
		return wikifyPlainText(theStore.getTiddlerText(title),limit,tiddler);
	} else {
		return "";
	}
}

function wikifyPlainText(text,limit,tiddler)
{
	if(limit > 0)
		text = text.substr(0,limit);
	var wikifier = new Wikifier(text,formatter,null,tiddler);
	return wikifier.wikifyPlain();
}

function highlightify(source,output,highlightRegExp,tiddler)
{
	if(source) {
		var wikifier = new Wikifier(source,formatter,highlightRegExp,tiddler);
		wikifier.outputText(output,0,source.length);
	}
}

function Wikifier(source,formatter,highlightRegExp,tiddler)
{
	this.source = source;
	this.output = null;
	this.formatter = formatter;
	this.nextMatch = 0;
	this.autoLinkWikiWords = tiddler && tiddler.autoLinkWikiWords() == false ? false : true;
	this.highlightRegExp = highlightRegExp;
	this.highlightMatch = null;
	this.isStatic = false;
	if(highlightRegExp) {
		highlightRegExp.lastIndex = 0;
		this.highlightMatch = highlightRegExp.exec(source);
	}
	this.tiddler = tiddler;
}

Wikifier.prototype.wikifyPlain = function()
{
	var e = createTiddlyElement(document.body,"div");
	e.style.display = "none";
	this.subWikify(e);
	var text = getPlainText(e);
	removeNode(e);
	return text;
};

Wikifier.prototype.subWikify = function(output,terminator)
{
	try {
		if(terminator)
			this.subWikifyTerm(output,new RegExp("(" + terminator + ")","mg"));
		else
			this.subWikifyUnterm(output);
	} catch(ex) {
		showException(ex);
	}
};

Wikifier.prototype.subWikifyUnterm = function(output)
{
	var oldOutput = this.output;
	this.output = output;
	this.formatter.formatterRegExp.lastIndex = this.nextMatch;
	var formatterMatch = this.formatter.formatterRegExp.exec(this.source);
	while(formatterMatch) {
		// Output any text before the match
		if(formatterMatch.index > this.nextMatch)
			this.outputText(this.output,this.nextMatch,formatterMatch.index);
		// Set the match parameters for the handler
		this.matchStart = formatterMatch.index;
		this.matchLength = formatterMatch[0].length;
		this.matchText = formatterMatch[0];
		this.nextMatch = this.formatter.formatterRegExp.lastIndex;
		for(var t=1; t<formatterMatch.length; t++) {
			if(formatterMatch[t]) {
				this.formatter.formatters[t-1].handler(this);
				this.formatter.formatterRegExp.lastIndex = this.nextMatch;
				break;
			}
		}
		formatterMatch = this.formatter.formatterRegExp.exec(this.source);
	}
	if(this.nextMatch < this.source.length) {
		this.outputText(this.output,this.nextMatch,this.source.length);
		this.nextMatch = this.source.length;
	}
	this.output = oldOutput;
};

Wikifier.prototype.subWikifyTerm = function(output,terminatorRegExp)
{
	var oldOutput = this.output;
	this.output = output;
	terminatorRegExp.lastIndex = this.nextMatch;
	var terminatorMatch = terminatorRegExp.exec(this.source);
	this.formatter.formatterRegExp.lastIndex = this.nextMatch;
	var formatterMatch = this.formatter.formatterRegExp.exec(terminatorMatch ? this.source.substr(0,terminatorMatch.index) : this.source);
	while(terminatorMatch || formatterMatch) {
		if(terminatorMatch && (!formatterMatch || terminatorMatch.index <= formatterMatch.index)) {
			if(terminatorMatch.index > this.nextMatch)
				this.outputText(this.output,this.nextMatch,terminatorMatch.index);
			this.matchText = terminatorMatch[1];
			this.matchLength = terminatorMatch[1].length;
			this.matchStart = terminatorMatch.index;
			this.nextMatch = this.matchStart + this.matchLength;
			this.output = oldOutput;
			return;
		}
		if(formatterMatch.index > this.nextMatch)
			this.outputText(this.output,this.nextMatch,formatterMatch.index);
		this.matchStart = formatterMatch.index;
		this.matchLength = formatterMatch[0].length;
		this.matchText = formatterMatch[0];
		this.nextMatch = this.formatter.formatterRegExp.lastIndex;
		for(var t=1; t<formatterMatch.length; t++) {
			if(formatterMatch[t]) {
				this.formatter.formatters[t-1].handler(this);
				this.formatter.formatterRegExp.lastIndex = this.nextMatch;
				break;
			}
		}
		terminatorRegExp.lastIndex = this.nextMatch;
		terminatorMatch = terminatorRegExp.exec(this.source);
		formatterMatch = this.formatter.formatterRegExp.exec(terminatorMatch ? this.source.substr(0,terminatorMatch.index) : this.source);
	}
	if(this.nextMatch < this.source.length) {
		this.outputText(this.output,this.nextMatch,this.source.length);
		this.nextMatch = this.source.length;
	}
	this.output = oldOutput;
};

Wikifier.prototype.outputText = function(place,startPos,endPos)
{
	while(this.highlightMatch && (this.highlightRegExp.lastIndex > startPos) && (this.highlightMatch.index < endPos) && (startPos < endPos)) {
		if(this.highlightMatch.index > startPos) {
			createTiddlyText(place,this.source.substring(startPos,this.highlightMatch.index));
			startPos = this.highlightMatch.index;
		}
		var highlightEnd = Math.min(this.highlightRegExp.lastIndex,endPos);
		var theHighlight = createTiddlyElement(place,"span",null,"highlight",this.source.substring(startPos,highlightEnd));
		startPos = highlightEnd;
		if(startPos >= this.highlightRegExp.lastIndex)
			this.highlightMatch = this.highlightRegExp.exec(this.source);
	}
	if(startPos < endPos) {
		createTiddlyText(place,this.source.substring(startPos,endPos));
	}
};

//--
//-- Macro definitions
//--

config.macros.today.handler = function(place,macroName,params)
{
	var now = new Date();
	var text = params[0] ? now.formatString(params[0].trim()) : now.toLocaleString();
	jQuery("<span/>").text(text).appendTo(place);
};

config.macros.version.handler = function(place)
{
	jQuery("<span/>").text(formatVersion()).appendTo(place);
};

config.macros.list.handler = function(place,macroName,params)
{
	var type = params[0] || "all";
	var list = document.createElement("ul");
	place.appendChild(list);
	if(this[type].prompt)
		createTiddlyElement(list,"li",null,"listTitle",this[type].prompt);
	var results;
	if(this[type].handler)
		results = this[type].handler(params);
	for(var t = 0; t < results.length; t++) {
		var li = document.createElement("li");
		list.appendChild(li);
		createTiddlyLink(li,typeof results[t] == "string" ? results[t] : results[t].title,true);
	}
};

config.macros.list.all.handler = function(params)
{
	return store.reverseLookup("tags","excludeLists",false,"title");
};

config.macros.list.missing.handler = function(params)
{
	return store.getMissingLinks();
};

config.macros.list.orphans.handler = function(params)
{
	return store.getOrphans();
};

config.macros.list.shadowed.handler = function(params)
{
	return store.getShadowed();
};

config.macros.list.touched.handler = function(params)
{
	return store.getTouched();
};

config.macros.list.filter.handler = function(params)
{
	var filter = params[1];
	var results = [];
	if(filter) {
		var tiddlers = store.filterTiddlers(filter);
		for(var t=0; t<tiddlers.length; t++)
			results.push(tiddlers[t].title);
	}
	return results;
};

config.macros.allTags.handler = function(place,macroName,params)
{
	var tags = store.getTags(params[0]);
	var ul = createTiddlyElement(place,"ul");
	if(tags.length == 0)
		createTiddlyElement(ul,"li",null,"listTitle",this.noTags);
	for(var t=0; t<tags.length; t++) {
		var title = tags[t][0];
		var info = getTiddlyLinkInfo(title);
		var li = createTiddlyElement(ul,"li");
		var btn = createTiddlyButton(li,title + " (" + tags[t][1] + ")",this.tooltip.format([title]),onClickTag,info.classes);
		btn.setAttribute("tag",title);
		btn.setAttribute("refresh","link");
		btn.setAttribute("tiddlyLink",title);
		if(params[1]) {
			btn.setAttribute("sortby",params[1]);
		}
	}
};

config.macros.timeline.handler = function(place,macroName,params)
{
	var field = params[0] || "modified";
	var tiddlers = store.reverseLookup("tags","excludeLists",false,field);
	var lastDay = "";
	var last = params[1] ? tiddlers.length-Math.min(tiddlers.length,parseInt(params[1])) : 0;
	var dateFormat = params[2] || this.dateFormat;
	for(var t=tiddlers.length-1; t>=last; t--) {
		var tiddler = tiddlers[t];
		var theDay = tiddler[field].convertToLocalYYYYMMDDHHMM().substr(0,8);
		if(theDay != lastDay) {
			var ul = document.createElement("ul");
			addClass(ul,"timeline");
			place.appendChild(ul);
			createTiddlyElement(ul,"li",null,"listTitle",tiddler[field].formatString(dateFormat));
			lastDay = theDay;
		}
		createTiddlyElement(ul,"li",null,"listLink").appendChild(createTiddlyLink(place,tiddler.title,true));
	}
};

config.macros.tiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
	var allowEval = true;
	var stack = config.macros.tiddler.tiddlerStack;
	if(stack.length > 0 && config.evaluateMacroParameters == "system") {
		// included tiddler and "system" evaluation required, so check tiddler tagged appropriately
		var title = stack[stack.length-1];
		var pos = title.indexOf(config.textPrimitives.sectionSeparator);
		if(pos != -1) {
			title = title.substr(0,pos); // get the base tiddler title
		}
		var t = store.getTiddler(title);
		if(!t || t.tags.indexOf("systemAllowEval") == -1) {
			allowEval = false;
		}
	}
	params = paramString.parseParams("name",null,allowEval,false,true);
	var names = params[0]["name"];
	var tiddlerName = names[0];
	var className = names[1] || null;
	var args = params[0]["with"];
	var wrapper = createTiddlyElement(place,"span",null,className,null,{
		refresh: "content", tiddler: tiddlerName
	});
	if(args!==undefined)
		wrapper.setAttribute("args","[["+args.join("]] [[")+"]]");
	this.transclude(wrapper,tiddlerName,args);
};

config.macros.tiddler.transclude = function(wrapper,tiddlerName,args)
{
	var text = store.getTiddlerText(tiddlerName);
	if(!text)
		return;
	var stack = config.macros.tiddler.tiddlerStack;
	if(stack.indexOf(tiddlerName) !== -1)
		return;
	stack.push(tiddlerName);
	try {
		if(typeof args == "string")
			args = args.readBracketedList();
		var n = args ? Math.min(args.length,9) : 0;
		for(var i=0; i<n; i++) {
			var placeholderRE = new RegExp("\\$" + (i + 1),"mg");
			text = text.replace(placeholderRE,args[i]);
		}
		config.macros.tiddler.renderText(wrapper,text,tiddlerName,params);
	} finally {
		stack.pop();
	}
};

config.macros.tiddler.renderText = function(place,text,tiddlerName,params)
{
	wikify(text,place,null,store.getTiddler(tiddlerName));
};

config.macros.tiddler.tiddlerStack = [];

config.macros.tag.handler = function(place,macroName,params)
{
	createTagButton(place,params[0],null,params[1],params[2]);
	if(params[3]) {
		btn.setAttribute('sortby',params[3]);
	}
};

config.macros.tags.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
	params = paramString.parseParams("anon",null,true,false,false);
	var ul = createTiddlyElement(place,"ul");
	var title = getParam(params,"anon","");
	if(title && store.tiddlerExists(title))
		tiddler = store.getTiddler(title);
	var sep = getParam(params,"sep"," ");
	var lingo = config.views.wikified.tag;
	var label = null;
	for(var t=0; t<tiddler.tags.length; t++) {
		var tag = store.getTiddler(tiddler.tags[t]);
		if(!tag || !tag.tags.contains("excludeLists")) {
			if(!label)
				label = createTiddlyElement(ul,"li",null,"listTitle",lingo.labelTags.format([tiddler.title]));
			createTagButton(createTiddlyElement(ul,"li"),tiddler.tags[t],tiddler.title);
			if(t<tiddler.tags.length-1)
				createTiddlyText(ul,sep);
		}
	}
	if(!label)
		createTiddlyElement(ul,"li",null,"listTitle",lingo.labelNoTags.format([tiddler.title]));
};

config.macros.tagging.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
	params = paramString.parseParams("anon",null,true,false,false);
	var ul = createTiddlyElement(place,"ul");
	var title = getParam(params,"anon","");
	if(title == "" && tiddler instanceof Tiddler)
		title = tiddler.title;
	var sep = getParam(params,"sep"," ");
	ul.setAttribute("title",this.tooltip.format([title]));
	var tagged = store.getTaggedTiddlers(title);
	var prompt = tagged.length == 0 ? this.labelNotTag : this.label;
	createTiddlyElement(ul,"li",null,"listTitle",prompt.format([title,tagged.length]));
	for(var t=0; t<tagged.length; t++) {
		createTiddlyLink(createTiddlyElement(ul,"li"),tagged[t].title,true);
		if(t<tagged.length-1)
			createTiddlyText(ul,sep);
	}
};

config.macros.closeAll.handler = function(place)
{
	createTiddlyButton(place,this.label,this.prompt,this.onClick);
};

config.macros.closeAll.onClick = function(e)
{
	story.closeAllTiddlers();
	return false;
};

config.macros.permaview.handler = function(place)
{
	createTiddlyButton(place,this.label,this.prompt,this.onClick);
};

config.macros.permaview.onClick = function(e)
{
	story.permaView();
	return false;
};

config.macros.saveChanges.handler = function(place,macroName,params)
{
	if(!readOnly)
		createTiddlyButton(place,params[0] || this.label,params[1] || this.prompt,this.onClick,null,null,this.accessKey);
};

config.macros.saveChanges.onClick = function(e)
{
	saveChanges();
	return false;
};

config.macros.slider.onClickSlider = function(ev)
{
	var e = ev || window.event;
	var n = this.nextSibling;
	var cookie = n.getAttribute("cookie");
	var isOpen = n.style.display != "none";
	if(config.options.chkAnimate && anim && typeof Slider == "function")
		anim.startAnimating(new Slider(n,!isOpen,null,"none"));
	else
		n.style.display = isOpen ? "none" : "block";
	config.options[cookie] = !isOpen;
	saveOption(cookie);
	return false;
};

config.macros.slider.createSlider = function(place,cookie,title,tooltip)
{
	var c = cookie || "";
	var btn = createTiddlyButton(place,title,tooltip,this.onClickSlider);
	var panel = createTiddlyElement(null,"div",null,"sliderPanel");
	panel.setAttribute("cookie",c);
	panel.style.display = config.options[c] ? "block" : "none";
	place.appendChild(panel);
	return panel;
};

config.macros.slider.handler = function(place,macroName,params)
{
	var panel = this.createSlider(place,params[0],params[2],params[3]);
	var text = store.getTiddlerText(params[1]);
	panel.setAttribute("refresh","content");
	panel.setAttribute("tiddler",params[1]);
	if(text)
		wikify(text,panel,null,store.getTiddler(params[1]));
};

// <<gradient [[tiddler name]] vert|horiz rgb rgb rgb rgb... >>
config.macros.gradient.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
	var panel = wikifier ? createTiddlyElement(place,"div",null,"gradient") : place;
	panel.style.position = "relative";
	panel.style.overflow = "hidden";
	panel.style.zIndex = "0";
	if(wikifier) {
		var styles = config.formatterHelpers.inlineCssHelper(wikifier);
		config.formatterHelpers.applyCssHelper(panel,styles);
	}
	params = paramString.parseParams("color");
	var locolors = [], hicolors = [];
	for(var t=2; t<params.length; t++) {
		var c = params[t].value;
		if(params[t].name == "snap") {
			hicolors[hicolors.length-1] = c;
		} else {
			locolors.push(c);
			hicolors.push(c);
		}
	}
	drawGradient(panel,params[1].value != "vert",locolors,hicolors);
	if(wikifier)
		wikifier.subWikify(panel,">>");
	if(document.all) {
		panel.style.height = "100%";
		panel.style.width = "100%";
	}
};

config.macros.message.handler = function(place,macroName,params)
{
	if(params[0]) {
		var names = params[0].split(".");
		var lookupMessage = function(root,nameIndex) {
				if(names[nameIndex] in root) {
					if(nameIndex < names.length-1)
						return (lookupMessage(root[names[nameIndex]],nameIndex+1));
					else
						return root[names[nameIndex]];
				} else
					return null;
			};
		var m = lookupMessage(config,0);
		if(m == null)
			m = lookupMessage(window,0);
		createTiddlyText(place,m.toString().format(params.splice(1)));
	}
};


config.macros.view.depth = 0;
config.macros.view.views = {
	text: function(value,place,params,wikifier,paramString,tiddler) {
		highlightify(value,place,highlightHack,tiddler);
	},
	link: function(value,place,params,wikifier,paramString,tiddler) {
		createTiddlyLink(place,value,true);
	},
	wikified: function(value,place,params,wikifier,paramString,tiddler) {
		if(config.macros.view.depth>50)
			return;
		config.macros.view.depth++;
		if(params[2])
			value=params[2].unescapeLineBreaks().format([value]);
		wikify(value,place,highlightHack,tiddler);
		config.macros.view.depth--;
	},
	date: function(value,place,params,wikifier,paramString,tiddler) {
		value = Date.convertFromYYYYMMDDHHMM(value);
		createTiddlyText(place,value.formatString(params[2] ? params[2] : config.views.wikified.dateFormat));
	}
};

config.macros.view.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
	if((tiddler instanceof Tiddler) && params[0]) {
		var value = store.getValue(tiddler,params[0]);
		if(value) {
			var type = params[1] || config.macros.view.defaultView;
			var handler = config.macros.view.views[type];
			if(handler)
				handler(value,place,params,wikifier,paramString,tiddler);
		}
	}
};

config.macros.edit.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
	var field = params[0];
	var rows = params[1] || 0;
	var defVal = params[2] || '';
	if((tiddler instanceof Tiddler) && field) {
		story.setDirty(tiddler.title,true);
		var e,v;
		if(field != "text" && !rows) {
			e = createTiddlyElement(null,"input",null,null,null,{
				type: "text", edit: field, size: "40", autocomplete: "off"
			});
			e.value = store.getValue(tiddler,field) || defVal;
			place.appendChild(e);
		} else {
			var wrapper1 = createTiddlyElement(null,"fieldset",null,"fieldsetFix");
			var wrapper2 = createTiddlyElement(wrapper1,"div");
			e = createTiddlyElement(wrapper2,"textarea");
			e.value = v = store.getValue(tiddler,field) || defVal;
			rows = rows || 10;
			var lines = v.match(/\n/mg);
			var maxLines = Math.max(parseInt(config.options.txtMaxEditRows),5);
			if(lines != null && lines.length > rows)
				rows = lines.length + 5;
			rows = Math.min(rows,maxLines);
			e.setAttribute("rows",rows);
			e.setAttribute("edit",field);
			place.appendChild(wrapper1);
		}
		if(tiddler.isReadOnly()) {
			e.setAttribute("readOnly","readOnly");
			addClass(e,"readOnly");
		}
		return e;
	}
};

config.macros.tagChooser.onClick = function(ev)
{
	var e = ev || window.event;
	var lingo = config.views.editor.tagChooser;
	var popup = Popup.create(this);
	var tags = store.getTags(this.getAttribute("tags"));
	if(tags.length == 0)
		jQuery("<li/>").text(lingo.popupNone).appendTo(popup);
	for(var t=0; t<tags.length; t++) {
		var tag = createTiddlyButton(createTiddlyElement(popup,"li"),tags[t][0],lingo.tagTooltip.format([tags[t][0]]),config.macros.tagChooser.onTagClick);
		tag.setAttribute("tag",tags[t][0]);
		tag.setAttribute("tiddler",this.getAttribute("tiddler"));
	}
	Popup.show();
	e.cancelBubble = true;
	if(e.stopPropagation) e.stopPropagation();
	return false;
};

config.macros.tagChooser.onTagClick = function(ev)
{
	var e = ev || window.event;
	if(e.metaKey || e.ctrlKey) stopEvent(e); //# keep popup open on CTRL-click
	var tag = this.getAttribute("tag");
	var title = this.getAttribute("tiddler");
	if(!readOnly)
		story.setTiddlerTag(title,tag,0);
	return false;
};

config.macros.tagChooser.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
	if(tiddler instanceof Tiddler) {
		var lingo = config.views.editor.tagChooser;
		var btn = createTiddlyButton(place,lingo.text,lingo.tooltip,this.onClick);
		btn.setAttribute("tiddler",tiddler.title);
		btn.setAttribute("tags",params[0]);
	}
};

config.macros.refreshDisplay.handler = function(place)
{
	createTiddlyButton(place,this.label,this.prompt,this.onClick);
};

config.macros.refreshDisplay.onClick = function(e)
{
	refreshAll();
	return false;
};

config.macros.annotations.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
	var title = tiddler ? tiddler.title : null;
	var a = title ? config.annotations[title] : null;
	if(!tiddler || !title || !a)
		return;
	var text = a.format([title]);
	wikify(text,createTiddlyElement(place,"div",null,"annotation"),null,tiddler);
};

//--
//-- NewTiddler and NewJournal macros
//--

config.macros.newTiddler.createNewTiddlerButton = function(place,title,params,label,prompt,accessKey,newFocus,isJournal)
{
	var tags = [];
	for(var t=1; t<params.length; t++) {
		if((params[t].name == "anon" && t != 1) || (params[t].name == "tag"))
			tags.push(params[t].value);
	}
	label = getParam(params,"label",label);
	prompt = getParam(params,"prompt",prompt);
	accessKey = getParam(params,"accessKey",accessKey);
	newFocus = getParam(params,"focus",newFocus);
	var customFields = getParam(params,"fields","");
	if(!customFields && !store.isShadowTiddler(title))
		customFields = String.encodeHashMap(config.defaultCustomFields);
	var btn = createTiddlyButton(place,label,prompt,this.onClickNewTiddler,null,null,accessKey);
	btn.setAttribute("newTitle",title);
	btn.setAttribute("isJournal",isJournal ? "true" : "false");
	if(tags.length > 0)
		btn.setAttribute("params",tags.join("|"));
	btn.setAttribute("newFocus",newFocus);
	btn.setAttribute("newTemplate",getParam(params,"template",DEFAULT_EDIT_TEMPLATE));
	if(customFields !== "")
		btn.setAttribute("customFields",customFields);
	var text = getParam(params,"text");
	if(text !== undefined)
		btn.setAttribute("newText",text);
	return btn;
};

config.macros.newTiddler.onClickNewTiddler = function()
{
	var title = this.getAttribute("newTitle");
	if(this.getAttribute("isJournal") == "true") {
		title = new Date().formatString(title.trim());
	}
	var params = this.getAttribute("params");
	var tags = params ? params.split("|") : [];
	var focus = this.getAttribute("newFocus");
	var template = this.getAttribute("newTemplate");
	var customFields = this.getAttribute("customFields");
	if(!customFields && !store.isShadowTiddler(title))
		customFields = String.encodeHashMap(config.defaultCustomFields);
	story.displayTiddler(null,title,template,false,null,null);
	var tiddlerElem = story.getTiddler(title);
	if(customFields)
		story.addCustomFields(tiddlerElem,customFields);
	var text = this.getAttribute("newText");
	if(typeof text == "string" && story.getTiddlerField(title,"text"))
		story.getTiddlerField(title,"text").value = text.format([title]);
	for(var t=0;t<tags.length;t++)
		story.setTiddlerTag(title,tags[t],+1);
	story.focusTiddler(title,focus);
	return false;
};

config.macros.newTiddler.handler = function(place,macroName,params,wikifier,paramString)
{
	if(!readOnly) {
		params = paramString.parseParams("anon",null,true,false,false);
		var title = params[1] && params[1].name == "anon" ? params[1].value : this.title;
		title = getParam(params,"title",title);
		this.createNewTiddlerButton(place,title,params,this.label,this.prompt,this.accessKey,"title",false);
	}
};

config.macros.newJournal.handler = function(place,macroName,params,wikifier,paramString)
{
	if(!readOnly) {
		params = paramString.parseParams("anon",null,true,false,false);
		var title = params[1] && params[1].name == "anon" ? params[1].value : config.macros.timeline.dateFormat;
		title = getParam(params,"title",title);
		config.macros.newTiddler.createNewTiddlerButton(place,title,params,this.label,this.prompt,this.accessKey,"text",true);
	}
};

//--
//-- Search macro
//--

config.macros.search.handler = function(place,macroName,params)
{
	var searchTimeout = null;
	var btn = createTiddlyButton(place,this.label,this.prompt,this.onClick,"searchButton");
	var txt = createTiddlyElement(null,"input",null,"txtOptionInput searchField");
	if(params[0]) {
		txt.value = params[0];
	}
	if(config.browser.isSafari) {
		txt.setAttribute("type","search");
		txt.setAttribute("results","5");
	} else {
		txt.setAttribute("type","text");
	}
	place.appendChild(txt);
	txt.onkeyup = this.onKeyPress;
	txt.onfocus = this.onFocus;
	txt.setAttribute("size",this.sizeTextbox);
	txt.setAttribute("accessKey",params[1] || this.accessKey);
	txt.setAttribute("autocomplete","off");
	txt.setAttribute("lastSearchText","");
};

// Global because there's only ever one outstanding incremental search timer
config.macros.search.timeout = null;

config.macros.search.doSearch = function(txt)
{
	if(txt.value.length > 0) {
		story.search(txt.value,config.options.chkCaseSensitiveSearch,config.options.chkRegExpSearch);
		txt.setAttribute("lastSearchText",txt.value);
	}
};

config.macros.search.onClick = function(e)
{
	config.macros.search.doSearch(this.nextSibling);
	return false;
};

config.macros.search.onKeyPress = function(ev)
{
	var me = config.macros.search;
	var e = ev || window.event;
	switch(e.keyCode) {
		case 9: // Tab
			return;
		case 13: // Ctrl-Enter
		case 10: // Ctrl-Enter on IE PC
			me.doSearch(this);
			break;
		case 27: // Escape
			this.value = "";
			clearMessage();
			break;
	}
	if(config.options.chkIncrementalSearch) {
		if(this.value.length > 2) {
			if(this.value != this.getAttribute("lastSearchText")) {
				if(me.timeout) {
					clearTimeout(me.timeout);
				}
				var txt = this;
				me.timeout = setTimeout(function() {me.doSearch(txt);},500);
			}
		} else {
			if(me.timeout) {
				clearTimeout(me.timeout);
			}
		}
	}
};

config.macros.search.onFocus = function(e)
{
	this.select();
};

//--
//-- Tabs macro
//--

config.macros.tabs.handler = function(place,macroName,params)
{
	var cookie = params[0];
	var numTabs = (params.length-1)/3;
	var wrapper = createTiddlyElement(null,"div",null,"tabsetWrapper " + cookie);
	var tabset = createTiddlyElement(wrapper,"div",null,"tabset");
	tabset.setAttribute("cookie",cookie);
	var validTab = false;
	for(var t=0; t<numTabs; t++) {
		var label = params[t*3+1];
		var prompt = params[t*3+2];
		var content = params[t*3+3];
		var tab = createTiddlyButton(tabset,label,prompt,this.onClickTab,"tab tabUnselected");
		createTiddlyElement(tab,"span",null,null," ",{style:"font-size:0pt;line-height:0px"});
		tab.setAttribute("tab",label);
		tab.setAttribute("content",content);
		tab.title = prompt;
		if(config.options[cookie] == label)
			validTab = true;
	}
	if(!validTab)
		config.options[cookie] = params[1];
	place.appendChild(wrapper);
	this.switchTab(tabset,config.options[cookie]);
};

config.macros.tabs.onClickTab = function(e)
{
	config.macros.tabs.switchTab(this.parentNode,this.getAttribute("tab"));
	return false;
};

config.macros.tabs.switchTab = function(tabset,tab)
{
	var cookie = tabset.getAttribute("cookie");
	var theTab = null;
	var nodes = tabset.childNodes;
	for(var t=0; t<nodes.length; t++) {
		if(nodes[t].getAttribute && nodes[t].getAttribute("tab") == tab) {
			theTab = nodes[t];
			theTab.className = "tab tabSelected";
		} else {
			nodes[t].className = "tab tabUnselected";
		}
	}
	if(theTab) {
		if(tabset.nextSibling && tabset.nextSibling.className == "tabContents")
			removeNode(tabset.nextSibling);
		var tabContent = createTiddlyElement(null,"div",null,"tabContents");
		tabset.parentNode.insertBefore(tabContent,tabset.nextSibling);
		var contentTitle = theTab.getAttribute("content");
		wikify(store.getTiddlerText(contentTitle),tabContent,null,store.getTiddler(contentTitle));
		if(cookie) {
			config.options[cookie] = tab;
			saveOption(cookie);
		}
	}
};

//--
//-- Tiddler toolbar
//--

// Create a toolbar command button
config.macros.toolbar.createCommand = function(place,commandName,tiddler,className)
{
	if(typeof commandName != "string") {
		var c = null;
		for(var t in config.commands) {
			if(config.commands[t] == commandName)
				c = t;
		}
		commandName = c;
	}
	if((tiddler instanceof Tiddler) && (typeof commandName == "string")) {
		var command = config.commands[commandName];
		if(command.isEnabled ? command.isEnabled(tiddler) : this.isCommandEnabled(command,tiddler)) {
			var text = command.getText ? command.getText(tiddler) : this.getCommandText(command,tiddler);
			var tooltip = command.getTooltip ? command.getTooltip(tiddler) : this.getCommandTooltip(command,tiddler);
			var cmd;
			switch(command.type) {
			case "popup":
				cmd = this.onClickPopup;
				break;
			case "command":
			default:
				cmd = this.onClickCommand;
				break;
			}
			var btn = createTiddlyButton(null,text,tooltip,cmd);
			btn.setAttribute("commandName",commandName);
			btn.setAttribute("tiddler",tiddler.title);
			addClass(btn,"command_" + commandName);
			if(className)
				addClass(btn,className);
			place.appendChild(btn);
		}
	}
};

config.macros.toolbar.isCommandEnabled = function(command,tiddler)
{
	var title = tiddler.title;
	var ro = tiddler.isReadOnly();
	var shadow = store.isShadowTiddler(title) && !store.tiddlerExists(title);
	return (!ro || (ro && !command.hideReadOnly)) && !(shadow && command.hideShadow);
};

config.macros.toolbar.getCommandText = function(command,tiddler)
{
	return tiddler.isReadOnly() && command.readOnlyText || command.text;
};

config.macros.toolbar.getCommandTooltip = function(command,tiddler)
{
	return tiddler.isReadOnly() && command.readOnlyTooltip || command.tooltip;
};

config.macros.toolbar.onClickCommand = function(ev)
{
	var e = ev || window.event;
	e.cancelBubble = true;
	if(e.stopPropagation) e.stopPropagation();
	var command = config.commands[this.getAttribute("commandName")];
	return command.handler(e,this,this.getAttribute("tiddler"));
};

config.macros.toolbar.onClickPopup = function(ev)
{
	var e = ev || window.event;
	e.cancelBubble = true;
	if(e.stopPropagation) e.stopPropagation();
	var popup = Popup.create(this);
	var command = config.commands[this.getAttribute("commandName")];
	var title = this.getAttribute("tiddler");
	var tiddler = store.fetchTiddler(title);
	popup.setAttribute("tiddler",title);
	command.handlePopup(popup,title);
	Popup.show();
	return false;
};

// Invoke the first command encountered from a given place that is tagged with a specified class
config.macros.toolbar.invokeCommand = function(place,className,event)
{
	var children = place.getElementsByTagName("a");
	for(var t=0; t<children.length; t++) {
		var c = children[t];
		if(hasClass(c,className) && c.getAttribute && c.getAttribute("commandName")) {
			if(c.onclick instanceof Function)
				c.onclick.call(c,event);
			break;
		}
	}
};

config.macros.toolbar.onClickMore = function(ev)
{
	var e = this.nextSibling;
	e.style.display = "inline";
	this.style.display = "none";
	return false;
};

config.macros.toolbar.onClickLess = function(ev)
{
	var e = this.parentNode;
	var m = e.previousSibling;
	e.style.display = "none";
	m.style.display = "inline";
	return false;
};

config.macros.toolbar.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
	for(var t=0; t<params.length; t++) {
		var c = params[t];
		switch(c) {
		case "!":
			createTiddlyText(place,this.separator);
			break;
		case "*":
			createTiddlyElement(place,"br");
			break;
		case "<":
			var btn = createTiddlyButton(place,this.lessLabel,this.lessPrompt,config.macros.toolbar.onClickLess);
			addClass(btn,"lessCommand");
			break;
		case ">":
			var btn = createTiddlyButton(place,this.moreLabel,this.morePrompt,config.macros.toolbar.onClickMore);
			addClass(btn,"moreCommand");
			var e = createTiddlyElement(place,"span",null,"moreCommand");
			e.style.display = "none";
			place = e;
			break;
		default:
			var className = "";
			switch(c.substr(0,1)) {
			case "+":
				className = "defaultCommand";
				c = c.substr(1);
				break;
			case "-":
				className = "cancelCommand";
				c = c.substr(1);
				break;
			}
			if(c in config.commands) {
				this.createCommand(place,c,tiddler,className);
			} else {
				this.customCommand(place,c,wikifier,tiddler);
			}
			break;
		}
	}
};

// Overrideable function to extend toolbar handler
config.macros.toolbar.customCommand = function(place,command,wikifier,tiddler)
{
}

//--
//-- Menu and toolbar commands
//--

config.commands.closeTiddler.handler = function(event,src,title)
{
	if(story.isDirty(title) && !readOnly) {
		if(!confirm(config.commands.cancelTiddler.warning.format([title])))
			return false;
	}
	story.setDirty(title,false);
	story.closeTiddler(title,true);
	return false;
};

config.commands.closeOthers.handler = function(event,src,title)
{
	story.closeAllTiddlers(title);
	return false;
};

config.commands.editTiddler.handler = function(event,src,title)
{
	clearMessage();
	var tiddlerElem = story.getTiddler(title);
	var fields = tiddlerElem.getAttribute("tiddlyFields");
	story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE,false,null,fields);
	var e = story.getTiddlerField(title,config.options.txtEditorFocus||"text");
	if(e) {
		setCaretPosition(e,0);
	}
	return false;
};

config.commands.saveTiddler.handler = function(event,src,title)
{
	var newTitle = story.saveTiddler(title,event.shiftKey);
	if(newTitle)
		story.displayTiddler(null,newTitle);
	return false;
};

config.commands.cancelTiddler.handler = function(event,src,title)
{
	if(story.hasChanges(title) && !readOnly) {
		if(!confirm(this.warning.format([title])))
			return false;
	}
	story.setDirty(title,false);
	story.displayTiddler(null,title);
	return false;
};

config.commands.deleteTiddler.handler = function(event,src,title)
{
	var deleteIt = true;
	if(config.options.chkConfirmDelete)
		deleteIt = confirm(this.warning.format([title]));
	if(deleteIt) {
		store.removeTiddler(title);
		story.closeTiddler(title,true);
		autoSaveChanges();
	}
	return false;
};

config.commands.permalink.handler = function(event,src,title)
{
	var t = encodeURIComponent(String.encodeTiddlyLink(title));
	if(window.location.hash != t)
		window.location.hash = t;
	return false;
};

config.commands.references.handlePopup = function(popup,title)
{
	var references = store.getReferringTiddlers(title);
	var c = false;
	for(var r=0; r<references.length; r++) {
		if(references[r].title != title && !references[r].isTagged("excludeLists")) {
			createTiddlyLink(createTiddlyElement(popup,"li"),references[r].title,true);
			c = true;
		}
	}
	if(!c)
		createTiddlyElement(popup,"li",null,"disabled",this.popupNone);
};

config.commands.jump.handlePopup = function(popup,title)
{
	story.forEachTiddler(function(title,element) {
		createTiddlyLink(createTiddlyElement(popup,"li"),title,true,null,false,null,true);
		});
};

config.commands.syncing.handlePopup = function(popup,title)
{
	var me = config.commands.syncing;
	var tiddler = store.fetchTiddler(title);
	if(!tiddler)
		return;
	var serverType = tiddler.getServerType();
	var serverHost = tiddler.fields["server.host"];
	var serverWorkspace = tiddler.fields["server.workspace"];
	if(!serverWorkspace)
		serverWorkspace = "";
	if(serverType) {
		var e = createTiddlyElement(popup,"li",null,"popupMessage");
		e.innerHTML = me.currentlySyncing.format([serverType,serverHost,serverWorkspace]);
	} else {
		createTiddlyElement(popup,"li",null,"popupMessage",me.notCurrentlySyncing);
	}
	if(serverType) {
		createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
		var btn = createTiddlyButton(createTiddlyElement(popup,"li"),this.captionUnSync,null,me.onChooseServer);
		btn.setAttribute("tiddler",title);
		btn.setAttribute("server.type","");
	}
	createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
	createTiddlyElement(popup,"li",null,"popupMessage",me.chooseServer);
	var feeds = store.getTaggedTiddlers("systemServer","title");
	for(var t=0; t<feeds.length; t++) {
		var f = feeds[t];
		var feedServerType = store.getTiddlerSlice(f.title,"Type");
		if(!feedServerType)
			feedServerType = "file";
		var feedServerHost = store.getTiddlerSlice(f.title,"URL");
		if(!feedServerHost)
			feedServerHost = "";
		var feedServerWorkspace = store.getTiddlerSlice(f.title,"Workspace");
		if(!feedServerWorkspace)
			feedServerWorkspace = "";
		var caption = f.title;
		if(serverType == feedServerType && serverHost == feedServerHost && serverWorkspace == feedServerWorkspace) {
			caption = me.currServerMarker + caption;
		} else {
			caption = me.notCurrServerMarker + caption;
		}
		btn = createTiddlyButton(createTiddlyElement(popup,"li"),caption,null,me.onChooseServer);
		btn.setAttribute("tiddler",title);
		btn.setAttribute("server.type",feedServerType);
		btn.setAttribute("server.host",feedServerHost);
		btn.setAttribute("server.workspace",feedServerWorkspace);
	}
};

config.commands.syncing.onChooseServer = function(e)
{
	var tiddler = this.getAttribute("tiddler");
	var serverType = this.getAttribute("server.type");
	if(serverType) {
		store.addTiddlerFields(tiddler,{
			"server.type": serverType,
			"server.host": this.getAttribute("server.host"),
			"server.workspace": this.getAttribute("server.workspace")
			});
	} else {
		store.setValue(tiddler,"server",null);
	}
	return false;
};

config.commands.fields.handlePopup = function(popup,title)
{
	var tiddler = store.fetchTiddler(title);
	if(!tiddler)
		return;
	var items = [];
	store.forEachField(tiddler,function(tiddler,fieldName,value){items.push({field:fieldName,value:value});},true);
	items.sort(function(a,b) {return a.field < b.field ? -1 : (a.field == b.field ? 0 : +1);});
	if(items.length > 0)
		ListView.create(popup,items,this.listViewTemplate);
	else
		createTiddlyElement(popup,"div",null,null,this.emptyText);
};

//--
//-- Tiddler() object
//--

function Tiddler(title)
{
	this.title = title;
	this.text = "";
	this.creator = null;
	this.modifier = null;
	this.created = new Date();
	this.modified = this.created;
	this.links = [];
	this.linksUpdated = false;
	this.tags = [];
	this.fields = {};
	return this;
}

Tiddler.prototype.getLinks = function()
{
	if(this.linksUpdated==false)
		this.changed();
	return this.links;
};

// Returns the fields that are inherited in string field:"value" field2:"value2" format
Tiddler.prototype.getInheritedFields = function()
{
	var f = {};
	for(var i in this.fields) {
		if(i=="server.host" || i=="server.workspace" || i=="wikiformat"|| i=="server.type") {
			f[i] = this.fields[i];
		}
	}
	return String.encodeHashMap(f);
};

// Increment the changeCount of a tiddler
Tiddler.prototype.incChangeCount = function()
{
	var c = this.fields['changecount'];
	c = c ? parseInt(c,10) : 0;
	this.fields['changecount'] = String(c+1);
};

// Clear the changeCount of a tiddler
Tiddler.prototype.clearChangeCount = function()
{
	if(this.fields['changecount']) {
		delete this.fields['changecount'];
	}
};

Tiddler.prototype.doNotSave = function()
{
	return this.fields['doNotSave'];
};

// Returns true if the tiddler has been updated since the tiddler was created or downloaded
Tiddler.prototype.isTouched = function()
{
	var changecount = this.fields.changecount || 0;
	return changecount > 0;
};

// Change the text and other attributes of a tiddler
Tiddler.prototype.set = function(title,text,modifier,modified,tags,created,fields,creator)
{
	this.assign(title,text,modifier,modified,tags,created,fields,creator);
	this.changed();
	return this;
};

// Change the text and other attributes of a tiddler without triggered a tiddler.changed() call
Tiddler.prototype.assign = function(title,text,modifier,modified,tags,created,fields,creator)
{
	if(title != undefined)
		this.title = title;
	if(text != undefined)
		this.text = text;
	if(modifier != undefined)
		this.modifier = modifier;
	if(modified != undefined)
		this.modified = modified;
	if(creator != undefined)
		this.creator = creator;
	if(created != undefined)
		this.created = created;
	if(fields != undefined)
		this.fields = fields;
	if(tags != undefined)
		this.tags = (typeof tags == "string") ? tags.readBracketedList() : tags;
	else if(this.tags == undefined)
		this.tags = [];
	return this;
};

// Get the tags for a tiddler as a string (space delimited, using [[brackets]] for tags containing spaces)
Tiddler.prototype.getTags = function()
{
	return String.encodeTiddlyLinkList(this.tags);
};

// Test if a tiddler carries a tag
Tiddler.prototype.isTagged = function(tag)
{
	return this.tags.indexOf(tag) != -1;
};

// Static method to convert "\n" to newlines, "\s" to "\"
Tiddler.unescapeLineBreaks = function(text)
{
	return text ? text.unescapeLineBreaks() : "";
};

// Convert newlines to "\n", "\" to "\s"
Tiddler.prototype.escapeLineBreaks = function()
{
	return this.text.escapeLineBreaks();
};

// Updates the secondary information (like links[] array) after a change to a tiddler
Tiddler.prototype.changed = function()
{
	this.links = [];
	var text = this.text;
	// remove 'quoted' text before scanning tiddler source
	text = text.replace(/\/%((?:.|\n)*?)%\//g,"").
		replace(/\{{3}((?:.|\n)*?)\}{3}/g,"").
		replace(/"""((?:.|\n)*?)"""/g,"").
		replace(/\<nowiki\>((?:.|\n)*?)\<\/nowiki\>/g,"").
		replace(/\<html\>((?:.|\n)*?)\<\/html\>/g,"").
		replace(/\<script((?:.|\n)*?)\<\/script\>/g,"");
	var t = this.autoLinkWikiWords() ? 0 : 1;
	var tiddlerLinkRegExp = t==0 ? config.textPrimitives.tiddlerAnyLinkRegExp : config.textPrimitives.tiddlerForcedLinkRegExp;
	tiddlerLinkRegExp.lastIndex = 0;
	var formatMatch = tiddlerLinkRegExp.exec(text);
	while(formatMatch) {
		var lastIndex = tiddlerLinkRegExp.lastIndex;
		if(t==0 && formatMatch[1] && formatMatch[1] != this.title) {
			// wikiWordLink
			if(formatMatch.index > 0) {
				var preRegExp = new RegExp(config.textPrimitives.unWikiLink+"|"+config.textPrimitives.anyLetter,"mg");
				preRegExp.lastIndex = formatMatch.index-1;
				var preMatch = preRegExp.exec(text);
				if(preMatch.index != formatMatch.index-1)
					this.links.pushUnique(formatMatch[1]);
			} else {
				this.links.pushUnique(formatMatch[1]);
			}
		}
		else if(formatMatch[2-t] && !config.formatterHelpers.isExternalLink(formatMatch[3-t])) // titledBrackettedLink
			this.links.pushUnique(formatMatch[3-t]);
		else if(formatMatch[4-t] && formatMatch[4-t] != this.title) // brackettedLink
			this.links.pushUnique(formatMatch[4-t]);
		tiddlerLinkRegExp.lastIndex = lastIndex;
		formatMatch = tiddlerLinkRegExp.exec(text);
	}
	this.linksUpdated = true;
};

Tiddler.prototype.getSubtitle = function()
{
	var modifier = this.modifier;
	if(!modifier)
		modifier = config.messages.subtitleUnknown;
	var modified = this.modified;
	if(modified)
		modified = modified.toLocaleString();
	else
		modified = config.messages.subtitleUnknown;
	return config.messages.tiddlerLinkTooltip.format([this.title,modifier,modified]);
};

Tiddler.prototype.isReadOnly = function()
{
	return readOnly;
};

Tiddler.prototype.autoLinkWikiWords = function()
{
	return !(this.isTagged("systemConfig") || this.isTagged("excludeMissing"));
};

Tiddler.prototype.getServerType = function()
{
	var serverType = null;
	if(this.fields['server.type'])
		serverType = this.fields['server.type'];
	if(!serverType)
		serverType = this.fields['wikiformat'];
	if(serverType && !config.adaptors[serverType])
		serverType = null;
	return serverType;
};

Tiddler.prototype.getAdaptor = function()
{
	var serverType = this.getServerType();
	return serverType ? new config.adaptors[serverType]() : null;
};

//--
//-- TiddlyWiki instance contains TiddlerS
//--

function TiddlyWiki()
{
	var tiddlers = {}; // Hashmap by name of tiddlers
	this.tiddlersUpdated = false;
	this.namedNotifications = []; // Array of {name:,notify:} of notification functions
	this.notificationLevel = 0;
	this.slices = {}; // map tiddlerName->(map sliceName->sliceValue). Lazy.
	this.clear = function() {
		tiddlers = {};
		this.setDirty(false);
	};
	this.fetchTiddler = function(title) {
		var t = tiddlers[title];
		return t instanceof Tiddler ? t : null;
	};
	this.deleteTiddler = function(title) {
		delete this.slices[title];
		delete tiddlers[title];
	};
	this.addTiddler = function(tiddler) {
		delete this.slices[tiddler.title];
		tiddlers[tiddler.title] = tiddler;
	};
	this.forEachTiddler = function(callback) {
		for(var t in tiddlers) {
			var tiddler = tiddlers[t];
			if(tiddler instanceof Tiddler)
				callback.call(this,t,tiddler);
		}
	};
}

TiddlyWiki.prototype.setDirty = function(dirty)
{
	this.dirty = dirty;
};

TiddlyWiki.prototype.isDirty = function()
{
	return this.dirty;
};

TiddlyWiki.prototype.tiddlerExists = function(title)
{
	var t = this.fetchTiddler(title);
	return t != undefined;
};

TiddlyWiki.prototype.isShadowTiddler = function(title)
{
	return config.shadowTiddlers[title] === undefined ? false : true;
};

TiddlyWiki.prototype.createTiddler = function(title)
{
	var tiddler = this.fetchTiddler(title);
	if(!tiddler) {
		tiddler = new Tiddler(title);
		this.addTiddler(tiddler);
		this.setDirty(true);
	}
	return tiddler;
};

TiddlyWiki.prototype.getTiddler = function(title)
{
	var t = this.fetchTiddler(title);
	if(t != undefined)
		return t;
	else
		return null;
};

TiddlyWiki.prototype.getShadowTiddlerText = function(title)
{
	if(typeof config.shadowTiddlers[title] == "string")
		return config.shadowTiddlers[title];
	else
		return "";
};

// Retrieve tiddler contents
TiddlyWiki.prototype.getTiddlerText = function(title,defaultText)
{
	if(!title)
		return defaultText;
	var pos = title.indexOf(config.textPrimitives.sectionSeparator);
	var section = null;
	if(pos != -1) {
		section = title.substr(pos + config.textPrimitives.sectionSeparator.length);
		title = title.substr(0,pos);
	}
	pos = title.indexOf(config.textPrimitives.sliceSeparator);
	if(pos != -1) {
		var slice = this.getTiddlerSlice(title.substr(0,pos),title.substr(pos + config.textPrimitives.sliceSeparator.length));
		if(slice)
			return slice;
	}
	var tiddler = this.fetchTiddler(title);
	var text = tiddler ? tiddler.text : null;
	if(!tiddler && this.isShadowTiddler(title)) {
		text = this.getShadowTiddlerText(title);
	}
	if(text) {
		if(!section)
			return text;
		var re = new RegExp("(^!{1,6}[ \t]*" + section.escapeRegExp() + "[ \t]*\n)","mg");
		re.lastIndex = 0;
		var match = re.exec(text);
		if(match) {
			var t = text.substr(match.index+match[1].length);
			var re2 = /^!/mg;
			re2.lastIndex = 0;
			match = re2.exec(t); //# search for the next heading
			if(match)
				t = t.substr(0,match.index-1);//# don't include final \n
			return t;
		}
		return defaultText;
	}
	if(defaultText != undefined)
		return defaultText;
	return null;
};

TiddlyWiki.prototype.getRecursiveTiddlerText = function(title,defaultText,depth)
{
	var bracketRegExp = new RegExp("(?:\\[\\[([^\\]]+)\\]\\])","mg");
	var text = this.getTiddlerText(title,null);
	if(text == null)
		return defaultText;
	var textOut = [];
	var lastPos = 0;
	do {
		var match = bracketRegExp.exec(text);
		if(match) {
			textOut.push(text.substr(lastPos,match.index-lastPos));
			if(match[1]) {
				if(depth <= 0)
					textOut.push(match[1]);
				else
					textOut.push(this.getRecursiveTiddlerText(match[1],"",depth-1));
			}
			lastPos = match.index + match[0].length;
		} else {
			textOut.push(text.substr(lastPos));
		}
	} while(match);
	return textOut.join("");
};

//TiddlyWiki.prototype.slicesRE = /(?:^([\'\/]{0,2})~?([\.\w]+)\:\1[\t\x20]*([^\n]+)[\t\x20]*$)|(?:^\|([\'\/]{0,2})~?([\.\w]+)\:?\4\|[\t\x20]*([^\n]+)[\t\x20]*\|$)/gm;
TiddlyWiki.prototype.slicesRE = /(?:^([\'\/]{0,2})~?([\.\w]+)\:\1[\t\x20]*([^\n]*)[\t\x20]*$)|(?:^\|([\'\/]{0,2})~?([\.\w]+)\:?\4\|[\t\x20]*([^\|\n]*)[\t\x20]*\|$)/gm;
// @internal
TiddlyWiki.prototype.calcAllSlices = function(title)
{
	var slices = {};
	var text = this.getTiddlerText(title,"");
	this.slicesRE.lastIndex = 0;
	var m = this.slicesRE.exec(text);
	while(m) {
		if(m[2])
			slices[m[2]] = m[3];
		else
			slices[m[5]] = m[6];
		m = this.slicesRE.exec(text);
	}
	return slices;
};

// Returns the slice of text of the given name
TiddlyWiki.prototype.getTiddlerSlice = function(title,sliceName)
{
	var slices = this.slices[title];
	if(!slices) {
		slices = this.calcAllSlices(title);
		this.slices[title] = slices;
	}
	return slices[sliceName];
};

// Build an hashmap of the specified named slices of a tiddler
TiddlyWiki.prototype.getTiddlerSlices = function(title,sliceNames)
{
	var r = {};
	for(var t=0; t<sliceNames.length; t++) {
		var slice = this.getTiddlerSlice(title,sliceNames[t]);
		if(slice)
			r[sliceNames[t]] = slice;
	}
	return r;
};

TiddlyWiki.prototype.suspendNotifications = function()
{
	this.notificationLevel--;
};

TiddlyWiki.prototype.resumeNotifications = function()
{
	this.notificationLevel++;
};

// Invoke the notification handlers for a particular tiddler
TiddlyWiki.prototype.notify = function(title,doBlanket)
{
	if(!this.notificationLevel) {
		for(var t=0; t<this.namedNotifications.length; t++) {
			var n = this.namedNotifications[t];
			if((n.name == null && doBlanket) || (n.name == title))
				n.notify(title);
		}
	}
};

// Invoke the notification handlers for all tiddlers
TiddlyWiki.prototype.notifyAll = function()
{
	if(!this.notificationLevel) {
		for(var t=0; t<this.namedNotifications.length; t++) {
			var n = this.namedNotifications[t];
			if(n.name)
				n.notify(n.name);
		}
	}
};

// Add a notification handler to a tiddler
TiddlyWiki.prototype.addNotification = function(title,fn)
{
	for(var i=0; i<this.namedNotifications.length; i++) {
		if((this.namedNotifications[i].name == title) && (this.namedNotifications[i].notify == fn))
			return this;
	}
	this.namedNotifications.push({name: title, notify: fn});
	return this;
};

TiddlyWiki.prototype.removeTiddler = function(title)
{
	var tiddler = this.fetchTiddler(title);
	if(tiddler) {
		this.deleteTiddler(title);
		this.notify(title,true);
		this.setDirty(true);
	}
};

// Reset the sync status of a freshly synced tiddler
TiddlyWiki.prototype.resetTiddler = function(title)
{
	var tiddler = this.fetchTiddler(title);
	if(tiddler) {
		tiddler.clearChangeCount();
		this.notify(title,true);
		this.setDirty(true);
	}
};

TiddlyWiki.prototype.setTiddlerTag = function(title,status,tag)
{
	var tiddler = this.fetchTiddler(title);
	if(tiddler) {
		var t = tiddler.tags.indexOf(tag);
		if(t != -1)
			tiddler.tags.splice(t,1);
		if(status)
			tiddler.tags.push(tag);
		tiddler.changed();
		tiddler.incChangeCount();
		this.notify(title,true);
		this.setDirty(true);
	}
};

TiddlyWiki.prototype.addTiddlerFields = function(title,fields)
{
	var tiddler = this.fetchTiddler(title);
	if(!tiddler)
		return;
	merge(tiddler.fields,fields);
	tiddler.changed();
	tiddler.incChangeCount();
	this.notify(title,true);
	this.setDirty(true);
};

// Store tiddler in TiddlyWiki instance
TiddlyWiki.prototype.saveTiddler = function(title,newTitle,newBody,modifier,modified,tags,fields,clearChangeCount,created,creator)
{
	var tiddler;
	if(title instanceof Tiddler) {
		tiddler = title;
		title = tiddler.title;
		newTitle = title;
	} else {
		tiddler = this.fetchTiddler(title);
		if(tiddler) {
			created = created || tiddler.created; // Preserve created date
			creator = creator || tiddler.creator;
			this.deleteTiddler(title);
		} else {
			created = created || modified;
			tiddler = new Tiddler();
		}
		fields = merge({},fields);
		tiddler.set(newTitle,newBody,modifier,modified,tags,created,fields,creator);
	}
	this.addTiddler(tiddler);
	if(clearChangeCount)
		tiddler.clearChangeCount();
	else
		tiddler.incChangeCount();
	if(title != newTitle)
		this.notify(title,true);
	this.notify(newTitle,true);
	this.setDirty(true);
	return tiddler;
};

TiddlyWiki.prototype.incChangeCount = function(title)
{
	var tiddler = this.fetchTiddler(title);
	if(tiddler)
		tiddler.incChangeCount();
};

TiddlyWiki.prototype.getLoader = function()
{
	if(!this.loader)
		this.loader = new TW21Loader();
	return this.loader;
};

TiddlyWiki.prototype.getSaver = function()
{
	if(!this.saver)
		this.saver = new TW21Saver();
	return this.saver;
};

// Return all tiddlers formatted as an HTML string
TiddlyWiki.prototype.allTiddlersAsHtml = function()
{
	return this.getSaver().externalize(store);
};

// Load contents of a TiddlyWiki from an HTML DIV
TiddlyWiki.prototype.loadFromDiv = function(src,idPrefix,noUpdate)
{
	this.idPrefix = idPrefix;
	var storeElem = (typeof src == "string") ? document.getElementById(src) : src;
	if(!storeElem)
		return;
	var tiddlers = this.getLoader().loadTiddlers(this,storeElem.childNodes);
	this.setDirty(false);
	if(!noUpdate) {
		for(var i = 0;i<tiddlers.length; i++)
			tiddlers[i].changed();
	}
	jQuery(document).trigger("loadTiddlers");
};

// Load contents of a TiddlyWiki from a string
// Returns null if there's an error
TiddlyWiki.prototype.importTiddlyWiki = function(text)
{
	var posDiv = locateStoreArea(text);
	if(!posDiv)
		return null;
	var content = "<" + "html><" + "body>" + text.substring(posDiv[0],posDiv[1] + endSaveArea.length) + "<" + "/body><" + "/html>";
	// Create the iframe
	var iframe = document.createElement("iframe");
	iframe.style.display = "none";
	document.body.appendChild(iframe);
	var doc = iframe.document;
	if(iframe.contentDocument)
		doc = iframe.contentDocument; // For NS6
	else if(iframe.contentWindow)
		doc = iframe.contentWindow.document; // For IE5.5 and IE6
	// Put the content in the iframe
	doc.open();
	doc.writeln(content);
	doc.close();
	// Load the content into a TiddlyWiki() object
	var storeArea = doc.getElementById("storeArea");
	this.loadFromDiv(storeArea,"store");
	// Get rid of the iframe
	iframe.parentNode.removeChild(iframe);
	return this;
};

TiddlyWiki.prototype.updateTiddlers = function()
{
	this.tiddlersUpdated = true;
	this.forEachTiddler(function(title,tiddler) {
		tiddler.changed();
	});
};

// Return an array of tiddlers matching a search regular expression
TiddlyWiki.prototype.search = function(searchRegExp,sortField,excludeTag,match)
{
	var candidates = this.reverseLookup("tags",excludeTag,!!match);
	var results = [];
	for(var t=0; t<candidates.length; t++) {
		if((candidates[t].title.search(searchRegExp) != -1) || (candidates[t].text.search(searchRegExp) != -1))
			results.push(candidates[t]);
	}
	if(!sortField)
		sortField = "title";
	results.sort(function(a,b) {return a[sortField] < b[sortField] ? -1 : (a[sortField] == b[sortField] ? 0 : +1);});
	return results;
};

// Returns a list of all tags in use
//   excludeTag - if present, excludes tags that are themselves tagged with excludeTag
// Returns an array of arrays where [tag][0] is the name of the tag and [tag][1] is the number of occurances
TiddlyWiki.prototype.getTags = function(excludeTag)
{
	var results = [];
	this.forEachTiddler(function(title,tiddler) {
		for(var g=0; g<tiddler.tags.length; g++) {
			var tag = tiddler.tags[g];
			var n = true;
			for(var c=0; c<results.length; c++) {
				if(results[c][0] == tag) {
					n = false;
					results[c][1]++;
				}
			}
			if(n && excludeTag) {
				var t = this.fetchTiddler(tag);
				if(t && t.isTagged(excludeTag))
					n = false;
			}
			if(n)
				results.push([tag,1]);
		}
	});
	results.sort(function(a,b) {return a[0].toLowerCase() < b[0].toLowerCase() ? -1 : (a[0].toLowerCase() == b[0].toLowerCase() ? 0 : +1);});
	return results;
};

// Return an array of the tiddlers that are tagged with a given tag
TiddlyWiki.prototype.getTaggedTiddlers = function(tag,sortField)
{
	return this.reverseLookup("tags",tag,true,sortField);
};

TiddlyWiki.prototype.getValueTiddlers = function(field,value,sortField)
{
	return this.reverseLookup(field,value,true,sortField);
};

// Return an array of the tiddlers that link to a given tiddler
TiddlyWiki.prototype.getReferringTiddlers = function(title,unusedParameter,sortField)
{
	if(!this.tiddlersUpdated)
		this.updateTiddlers();
	return this.reverseLookup("links",title,true,sortField);
};

// Return an array of the tiddlers that do or do not have a specified entry in the specified storage array (ie, "links" or "tags")
// lookupMatch == true to match tiddlers, false to exclude tiddlers
TiddlyWiki.prototype.reverseLookup = function(lookupField,lookupValue,lookupMatch,sortField)
{
	var results = [];
	this.forEachTiddler(function(title,tiddler) {
		var f = !lookupMatch;
		var values;
		if(["links", "tags"].contains(lookupField)) {
			values = tiddler[lookupField];
		} else {
			values = tiddler.fields[lookupField] ? [tiddler.fields[lookupField]] : [];
		}
		for(var lookup=0; lookup<values.length; lookup++) {
			if(values[lookup] == lookupValue)
				f = lookupMatch;
		}
		if(f)
			results.push(tiddler);
	});
	if(!sortField)
		sortField = "title";
	return this.sortTiddlers(results,sortField);
};

// Return the tiddlers as a sorted array
TiddlyWiki.prototype.getTiddlers = function(field,excludeTag)
{
	var results = [];
	this.forEachTiddler(function(title,tiddler) {
		if(excludeTag == undefined || !tiddler.isTagged(excludeTag))
			results.push(tiddler);
	});
	if(field)
		results.sort(function(a,b) {return a[field] < b[field] ? -1 : (a[field] == b[field] ? 0 : +1);});
	return results;
};

// Return array of names of tiddlers that are referred to but not defined
TiddlyWiki.prototype.getMissingLinks = function(sortField)
{
	if(!this.tiddlersUpdated)
		this.updateTiddlers();
	var results = [];
	this.forEachTiddler(function (title,tiddler) {
		if(tiddler.isTagged("excludeMissing") || tiddler.isTagged("systemConfig"))
			return;
		for(var n=0; n<tiddler.links.length;n++) {
			var link = tiddler.links[n];
			if(this.getTiddlerText(link,null) == null && !this.isShadowTiddler(link) && !config.macros[link])
				results.pushUnique(link);
		}
	});
	results.sort();
	return results;
};

// Return an array of names of tiddlers that are defined but not referred to
TiddlyWiki.prototype.getOrphans = function()
{
	var results = [];
	this.forEachTiddler(function (title,tiddler) {
		if(this.getReferringTiddlers(title).length == 0 && !tiddler.isTagged("excludeLists"))
			results.push(title);
	});
	results.sort();
	return results;
};

// Return an array of names of all the shadow tiddlers
TiddlyWiki.prototype.getShadowed = function()
{
	var results = [];
	for(var t in config.shadowTiddlers) {
		if(this.isShadowTiddler(t))
			results.push(t);
	}
	results.sort();
	return results;
};

// Return an array of tiddlers that have been touched since they were downloaded or created
TiddlyWiki.prototype.getTouched = function()
{
	var results = [];
	this.forEachTiddler(function(title,tiddler) {
		if(tiddler.isTouched())
			results.push(tiddler);
		});
	results.sort();
	return results;
};

// Resolves a Tiddler reference or tiddler title into a Tiddler object, or null if it doesn't exist
TiddlyWiki.prototype.resolveTiddler = function(tiddler)
{
	var t = (typeof tiddler == "string") ? this.getTiddler(tiddler) : tiddler;
	return t instanceof Tiddler ? t : null;
};

// Sort a list of tiddlers
TiddlyWiki.prototype.sortTiddlers = function(tiddlers,field)
{
	var asc = +1;
	switch(field.substr(0,1)) {
	case "-":
		asc = -1;
		// Note: this fall-through is intentional
		/*jsl:fallthru*/
	case "+":
		field = field.substr(1);
		break;
	}
	if(TiddlyWiki.standardFieldAccess[field]) {
		if(field=="title") {
			tiddlers.sort(function(a,b) {return a[field].toLowerCase() < b[field].toLowerCase() ? -asc : (a[field].toLowerCase() == b[field].toLowerCase() ? 0 : asc);});
		} else {
			tiddlers.sort(function(a,b) {return a[field] < b[field] ? -asc : (a[field] == b[field] ? 0 : asc);});
		}
	} else {
		tiddlers.sort(function(a,b) {return a.fields[field] < b.fields[field] ? -asc : (a.fields[field] == b.fields[field] ? 0 : +asc);});
	}
	return tiddlers;
};

//--
//-- Filter a list of tiddlers
//--

config.filters = {
	tiddler: function(results,match) {
		var title = match[1]||match[4];
		var tiddler = this.fetchTiddler(title);
		if(tiddler) {
			results.pushUnique(tiddler);
		} else if(this.isShadowTiddler(title)) {
			tiddler = new Tiddler();
			tiddler.set(title,this.getTiddlerText(title));
			results.pushUnique(tiddler);
		} else {
			results.pushUnique(new Tiddler(title));
		}
		return results;
	},
	tag: function(results,match) {
		var matched = this.getTaggedTiddlers(match[3]);
		for(var m=0; m<matched.length; m++) {
			results.pushUnique(matched[m]);
		}
		return results;
	},
	sort: function(results,match) {
		return this.sortTiddlers(results,match[3]);
	},
	limit: function(results,match) {
		return results.slice(0,parseInt(match[3],10));
	},
	field: function(results,match) {
		var matched = this.getValueTiddlers(match[2],match[3]);
		for (var m = 0; m < matched.length; m++) {
			results.pushUnique(matched[m]);
		}
		return results;
	}
};

// Filter a list of tiddlers
TiddlyWiki.prototype.filterTiddlers = function(filter)
{
	var re = /([^\s\[\]]+)|(?:\[([ \w\.\-]+)\[([^\]]+)\]\])|(?:\[\[([^\]]+)\]\])/mg;

	var results = [];
	if(filter) {
		var match = re.exec(filter);
		while(match) {
			var handler = (match[1]||match[4])?'tiddler':config.filters[match[2]]?match[2]:'field';
			results = config.filters[handler].call(this,results,match);
			match = re.exec(filter);
		}
	}
	return results;
};
// Returns true if path is a valid field name (path),
// i.e. a sequence of identifiers, separated by "."
TiddlyWiki.isValidFieldName = function(name)
{
	var match = /[a-zA-Z_]\w*(\.[a-zA-Z_]\w*)*/.exec(name);
	return match && (match[0] == name);
};

// Throws an exception when name is not a valid field name.
TiddlyWiki.checkFieldName = function(name)
{
	if(!TiddlyWiki.isValidFieldName(name))
		throw config.messages.invalidFieldName.format([name]);
};

function StringFieldAccess(n,readOnly)
{
	this.set = readOnly ?
			function(t,v) {if(v != t[n]) throw config.messages.fieldCannotBeChanged.format([n]);} :
			function(t,v) {if(v != t[n]) {t[n] = v; return true;}};
	this.get = function(t) {return t[n];};
}

function DateFieldAccess(n)
{
	this.set = function(t,v) {
		var d = v instanceof Date ? v : Date.convertFromYYYYMMDDHHMM(v);
		if(d != t[n]) {
			t[n] = d; return true;
		}
	};
	this.get = function(t) {return t[n].convertToYYYYMMDDHHMM();};
}

function LinksFieldAccess(n)
{
	this.set = function(t,v) {
		var s = (typeof v == "string") ? v.readBracketedList() : v;
		if(s.toString() != t[n].toString()) {
			t[n] = s; return true;
		}
	};
	this.get = function(t) {return String.encodeTiddlyLinkList(t[n]);};
}

TiddlyWiki.standardFieldAccess = {
	// The set functions return true when setting the data has changed the value.
	"title":    new StringFieldAccess("title",true),
	// Handle the "tiddler" field name as the title
	"tiddler":  new StringFieldAccess("title",true),
	"text":     new StringFieldAccess("text"),
	"modifier": new StringFieldAccess("modifier"),
	"modified": new DateFieldAccess("modified"),
	"creator":  new StringFieldAccess("creator"),
	"created":  new DateFieldAccess("created"),
	"tags":     new LinksFieldAccess("tags")
};

TiddlyWiki.isStandardField = function(name)
{
	return TiddlyWiki.standardFieldAccess[name] != undefined;
};

// Sets the value of the given field of the tiddler to the value.
// Setting an ExtendedField's value to null or undefined removes the field.
// Setting a namespace to undefined removes all fields of that namespace.
// The fieldName is case-insensitive.
// All values will be converted to a string value.
TiddlyWiki.prototype.setValue = function(tiddler,fieldName,value)
{
	TiddlyWiki.checkFieldName(fieldName);
	var t = this.resolveTiddler(tiddler);
	if(!t)
		return;
	fieldName = fieldName.toLowerCase();
	var isRemove = (value === undefined) || (value === null);
	var accessor = TiddlyWiki.standardFieldAccess[fieldName];
	if(accessor) {
		if(isRemove)
			// don't remove StandardFields
			return;
		var h = TiddlyWiki.standardFieldAccess[fieldName];
		if(!h.set(t,value))
			return;
	} else {
		var oldValue = t.fields[fieldName];
		if(isRemove) {
			if(oldValue !== undefined) {
				// deletes a single field
				delete t.fields[fieldName];
			} else {
				// no concrete value is defined for the fieldName
				// so we guess this is a namespace path.
				// delete all fields in a namespace
				var re = new RegExp("^"+fieldName+"\\.");
				var dirty = false;
				for(var n in t.fields) {
					if(n.match(re)) {
						delete t.fields[n];
						dirty = true;
					}
				}
				if(!dirty)
					return;
			}
		} else {
			// the "normal" set case. value is defined (not null/undefined)
			// For convenience provide a nicer conversion Date->String
			value = value instanceof Date ? value.convertToYYYYMMDDHHMMSSMMM() : String(value);
			if(oldValue == value)
				return;
			t.fields[fieldName] = value;
		}
	}
	// When we are here the tiddler/store really was changed.
	this.notify(t.title,true);
	if(!fieldName.match(/^temp\./))
		this.setDirty(true);
};

// Returns the value of the given field of the tiddler.
// The fieldName is case-insensitive.
// Will only return String values (or undefined).
TiddlyWiki.prototype.getValue = function(tiddler,fieldName)
{
	var t = this.resolveTiddler(tiddler);
	if(!t)
		return undefined;
	fieldName = fieldName.toLowerCase();
	var accessor = TiddlyWiki.standardFieldAccess[fieldName];
	if(accessor) {
		return accessor.get(t);
	}
	return t.fields[fieldName];
};

// Calls the callback function for every field in the tiddler.
// When callback function returns a non-false value the iteration stops
// and that value is returned.
// The order of the fields is not defined.
// @param callback a function(tiddler,fieldName,value).
TiddlyWiki.prototype.forEachField = function(tiddler,callback,onlyExtendedFields)
{
	var t = this.resolveTiddler(tiddler);
	if(!t)
		return undefined;
	var n,result;
	for(n in t.fields) {
		result = callback(t,n,t.fields[n]);
		if(result)
			return result;
		}
	if(onlyExtendedFields)
		return undefined;
	for(n in TiddlyWiki.standardFieldAccess) {
		if(n == "tiddler")
			// even though the "title" field can also be referenced through the name "tiddler"
			// we only visit this field once.
			continue;
		result = callback(t,n,TiddlyWiki.standardFieldAccess[n].get(t));
		if(result)
			return result;
	}
	return undefined;
};

//--
//-- Story functions
//--

function Story(containerId,idPrefix)
{
	this.container = containerId;
	this.idPrefix = idPrefix;
	this.highlightRegExp = null;
	this.tiddlerId = function(title) {
		var id = this.idPrefix + title;
		return id==this.container ? this.idPrefix + "_" + title : id;
	};
	this.containerId = function() {
		return this.container;
	};
}

Story.prototype.getTiddler = function(title)
{
	return document.getElementById(this.tiddlerId(title));
};

Story.prototype.getContainer = function()
{
	return document.getElementById(this.containerId());
};

Story.prototype.forEachTiddler = function(fn)
{
	var place = this.getContainer();
	if(!place)
		return;
	var e = place.firstChild;
	while(e) {
		var n = e.nextSibling;
		var title = e.getAttribute("tiddler");
		fn.call(this,title,e);
		e = n;
	}
};

Story.prototype.displayDefaultTiddlers = function()
{
	this.displayTiddlers(null,store.filterTiddlers(store.getTiddlerText("DefaultTiddlers")));
};

Story.prototype.displayTiddlers = function(srcElement,titles,template,animate,unused,customFields,toggle)
{
	for(var t = titles.length-1;t>=0;t--)
		this.displayTiddler(srcElement,titles[t],template,animate,unused,customFields);
};

Story.prototype.displayTiddler = function(srcElement,tiddler,template,animate,unused,customFields,toggle,animationSrc)
{
	var title = (tiddler instanceof Tiddler) ? tiddler.title : tiddler;
	var tiddlerElem = this.getTiddler(title);
	if(tiddlerElem) {
		if(toggle) {
			if(tiddlerElem.getAttribute("dirty") != "true")
				this.closeTiddler(title,true);
		} else {
			this.refreshTiddler(title,template,false,customFields);
		}
	} else {
		var place = this.getContainer();
		var before = this.positionTiddler(srcElement);
		tiddlerElem = this.createTiddler(place,before,title,template,customFields);
	}
	if(animationSrc && typeof animationSrc !== "string") {
		srcElement = animationSrc;
	}
	if(srcElement && typeof srcElement !== "string") {
		if(config.options.chkAnimate && (animate == undefined || animate == true) && anim && typeof Zoomer == "function" && typeof Scroller == "function")
			anim.startAnimating(new Zoomer(title,srcElement,tiddlerElem),new Scroller(tiddlerElem));
		else
			window.scrollTo(0,ensureVisible(tiddlerElem));
	}
	return tiddlerElem;
};

Story.prototype.positionTiddler = function(srcElement)
{
	var place = this.getContainer();
	var before = null;
	if(typeof srcElement == "string") {
		switch(srcElement) {
		case "top":
			before = place.firstChild;
			break;
		case "bottom":
			before = null;
			break;
		}
	} else {
		var after = this.findContainingTiddler(srcElement);
		if(after == null) {
			before = place.firstChild;
		} else if(after.nextSibling) {
			before = after.nextSibling;
			if(before.nodeType != 1)
				before = null;
		}
	}
	return before;
};

Story.prototype.createTiddler = function(place,before,title,template,customFields)
{
	var tiddlerElem = createTiddlyElement(null,"div",this.tiddlerId(title),"tiddler");
	tiddlerElem.setAttribute("refresh","tiddler");
	if(customFields)
		tiddlerElem.setAttribute("tiddlyFields",customFields);
	place.insertBefore(tiddlerElem,before);
	var defaultText = null;
	if(!store.tiddlerExists(title) && !store.isShadowTiddler(title))
		defaultText = this.loadMissingTiddler(title,customFields);
	this.refreshTiddler(title,template,false,customFields,defaultText);
	return tiddlerElem;
};

Story.prototype.loadMissingTiddler = function(title,fields,callback)
{
	var getTiddlerCallback = function(context)
	{
		if(context.status) {
			var t = context.tiddler;
			if(!t.created)
				t.created = new Date();
			if(!t.modified)
				t.modified = t.created;
			context.tiddler = store.saveTiddler(t.title,t.title,t.text,t.modifier,t.modified,t.tags,t.fields,true,t.created,t.creator);
			autoSaveChanges();
		} else {
			story.refreshTiddler(context.title,null,true);
		}
		context.adaptor.close();
		if(callback) {
			callback(context);
		}
	};
	var tiddler = new Tiddler(title);
	tiddler.fields = typeof fields == "string" ? fields.decodeHashMap() : fields||{};
	var context = {serverType:tiddler.getServerType()};
	if(!context.serverType)
		return;
	context.host = tiddler.fields['server.host'];
	context.workspace = tiddler.fields['server.workspace'];
	var adaptor = new config.adaptors[context.serverType];
	adaptor.getTiddler(title,context,null,getTiddlerCallback);
	return config.messages.loadingMissingTiddler.format([title,context.serverType,context.host,context.workspace]);
};

Story.prototype.chooseTemplateForTiddler = function(title,template)
{
	if(!template)
		template = DEFAULT_VIEW_TEMPLATE;
	if(template == DEFAULT_VIEW_TEMPLATE || template == DEFAULT_EDIT_TEMPLATE)
		template = config.tiddlerTemplates[template];
	return template;
};

Story.prototype.getTemplateForTiddler = function(title,template,tiddler)
{
	return store.getRecursiveTiddlerText(template,null,10);
};

Story.prototype.refreshTiddler = function(title,template,force,customFields,defaultText)
{
	var tiddlerElem = this.getTiddler(title);
	if(tiddlerElem) {
		if(tiddlerElem.getAttribute("dirty") == "true" && !force)
			return tiddlerElem;
		template = this.chooseTemplateForTiddler(title,template);
		var currTemplate = tiddlerElem.getAttribute("template");
		if((template != currTemplate) || force) {
			var tiddler = store.getTiddler(title);
			if(!tiddler) {
				tiddler = new Tiddler();
				if(store.isShadowTiddler(title)) {
					var tags = [];
					tiddler.set(title,store.getTiddlerText(title),config.views.wikified.shadowModifier,version.date,tags,version.date);
				} else {
					var text = template=="EditTemplate" ?
								config.views.editor.defaultText.format([title]) :
								config.views.wikified.defaultText.format([title]);
					text = defaultText || text;
					var fields = customFields ? customFields.decodeHashMap() : null;
					tiddler.set(title,text,config.views.wikified.defaultModifier,version.date,[],version.date,fields);
				}
			}
			tiddlerElem.setAttribute("tags",tiddler.tags.join(" "));
			tiddlerElem.setAttribute("tiddler",title);
			tiddlerElem.setAttribute("template",template);
			tiddlerElem.onmouseover = this.onTiddlerMouseOver;
			tiddlerElem.onmouseout = this.onTiddlerMouseOut;
			tiddlerElem.ondblclick = this.onTiddlerDblClick;
			tiddlerElem[window.event?"onkeydown":"onkeypress"] = this.onTiddlerKeyPress;
			tiddlerElem.innerHTML = this.getTemplateForTiddler(title,template,tiddler);
			applyHtmlMacros(tiddlerElem,tiddler);
			if(store.getTaggedTiddlers(title).length > 0)
				addClass(tiddlerElem,"isTag");
			else
				removeClass(tiddlerElem,"isTag");
			if(store.tiddlerExists(title)) {
				removeClass(tiddlerElem,"shadow");
				removeClass(tiddlerElem,"missing");
			} else {
				addClass(tiddlerElem, store.isShadowTiddler(title) ? "shadow" : "missing");
			}
			if(customFields)
				this.addCustomFields(tiddlerElem,customFields);
			forceReflow();
		}
	}
	return tiddlerElem;
};

Story.prototype.addCustomFields = function(place,customFields)
{
	var fields = customFields.decodeHashMap();
	var w = createTiddlyElement(place,"div",null,"customFields");
	w.style.display = "none";
	for(var t in fields) {
		var e = document.createElement("input");
		e.setAttribute("type","text");
		e.setAttribute("value",fields[t]);
		w.appendChild(e);
		e.setAttribute("edit",t);
	}
};

Story.prototype.refreshAllTiddlers = function(force)
{
	var e = this.getContainer().firstChild;
	while(e) {
		var template = e.getAttribute("template");
		if(template && e.getAttribute("dirty") != "true") {
			this.refreshTiddler(e.getAttribute("tiddler"),force ? null : template,true);
		}
		e = e.nextSibling;
	}
};

Story.prototype.onTiddlerMouseOver = function(e)
{
	addClass(this, "selected");
};

Story.prototype.onTiddlerMouseOut = function(e)
{
	removeClass(this,"selected");
};

Story.prototype.onTiddlerDblClick = function(ev)
{
	var e = ev || window.event;
	var target = resolveTarget(e);
	if(target && target.nodeName.toLowerCase() != "input" && target.nodeName.toLowerCase() != "textarea") {
		if(document.selection && document.selection.empty)
			document.selection.empty();
		config.macros.toolbar.invokeCommand(this,"defaultCommand",e);
		e.cancelBubble = true;
		if(e.stopPropagation) e.stopPropagation();
		return true;
	}
	return false;
};

Story.prototype.onTiddlerKeyPress = function(ev)
{
	var e = ev || window.event;
	clearMessage();
	var consume = false;
	var title = this.getAttribute("tiddler");
	var target = resolveTarget(e);
	switch(e.keyCode) {
	case 9: // Tab
		var ed = story.getTiddlerField(title,"text");
		if(target.tagName.toLowerCase() == "input" && ed.value==config.views.editor.defaultText.format([title])) {
			// moving from input field and editor still contains default text, so select it
			ed.focus();
			ed.select();
			consume = true;
		}
		if(config.options.chkInsertTabs && target.tagName.toLowerCase() == "textarea") {
			replaceSelection(target,String.fromCharCode(9));
			consume = true;
		}
		if(config.isOpera) {
			target.onblur = function() {
				this.focus();
				this.onblur = null;
			};
		}
		break;
	case 13: // Ctrl-Enter
	case 10: // Ctrl-Enter on IE PC
	case 77: // Ctrl-Enter is "M" on some platforms
		if(e.ctrlKey) {
			blurElement(this);
			config.macros.toolbar.invokeCommand(this,"defaultCommand",e);
			consume = true;
		}
		break;
	case 27: // Escape
		blurElement(this);
		config.macros.toolbar.invokeCommand(this,"cancelCommand",e);
		consume = true;
		break;
	}
	e.cancelBubble = consume;
	if(consume) {
		if(e.stopPropagation) e.stopPropagation(); // Stop Propagation
		e.returnValue = true; // Cancel The Event in IE
		if(e.preventDefault ) e.preventDefault(); // Cancel The Event in Moz
	}
	return !consume;
};

Story.prototype.getTiddlerField = function(title,field)
{
	var tiddlerElem = this.getTiddler(title);
	var e = null;
	if(tiddlerElem) {
		var children = tiddlerElem.getElementsByTagName("*");
		for(var t=0; t<children.length; t++) {
			var c = children[t];
			if(c.tagName.toLowerCase() == "input" || c.tagName.toLowerCase() == "textarea") {
				if(!e)
					e = c;
				if(c.getAttribute("edit") == field)
					e = c;
			}
		}
	}
	return e;
};

Story.prototype.focusTiddler = function(title,field)
{
	var e = this.getTiddlerField(title,field);
	if(e) {
		e.focus();
		e.select();
	}
};

Story.prototype.blurTiddler = function(title)
{
	var tiddlerElem = this.getTiddler(title);
	if(tiddlerElem && tiddlerElem.focus && tiddlerElem.blur) {
		tiddlerElem.focus();
		tiddlerElem.blur();
	}
};

Story.prototype.setTiddlerField = function(title,tag,mode,field)
{
	var c = this.getTiddlerField(title,field);
	var tags = c.value.readBracketedList();
	tags.setItem(tag,mode);
	c.value = String.encodeTiddlyLinkList(tags);
};

Story.prototype.setTiddlerTag = function(title,tag,mode)
{
	this.setTiddlerField(title,tag,mode,"tags");
};

Story.prototype.closeTiddler = function(title,animate,unused)
{
	var tiddlerElem = this.getTiddler(title);
	if(tiddlerElem) {
		clearMessage();
		this.scrubTiddler(tiddlerElem);
		if(config.options.chkAnimate && animate && anim && typeof Slider == "function")
			anim.startAnimating(new Slider(tiddlerElem,false,null,"all"));
		else {
			removeNode(tiddlerElem);
			forceReflow();
		}
	}
};

Story.prototype.scrubTiddler = function(tiddlerElem)
{
	tiddlerElem.id = null;
};

Story.prototype.setDirty = function(title,dirty)
{
	var tiddlerElem = this.getTiddler(title);
	if(tiddlerElem)
		tiddlerElem.setAttribute("dirty",dirty ? "true" : "false");
};

Story.prototype.isDirty = function(title)
{
	var tiddlerElem = this.getTiddler(title);
	if(tiddlerElem)
		return tiddlerElem.getAttribute("dirty") == "true";
	return null;
};

Story.prototype.areAnyDirty = function()
{
	var r = false;
	this.forEachTiddler(function(title,element) {
		if(this.isDirty(title))
			r = true;
	});
	return r;
};

Story.prototype.closeAllTiddlers = function(exclude)
{
	clearMessage();
	this.forEachTiddler(function(title,element) {
		if((title != exclude) && element.getAttribute("dirty") != "true")
			this.closeTiddler(title);
	});
	window.scrollTo(0,ensureVisible(this.container));
};

Story.prototype.isEmpty = function()
{
	var place = this.getContainer();
	return place && place.firstChild == null;
};

Story.prototype.search = function(text,useCaseSensitive,useRegExp)
{
	this.closeAllTiddlers();
	highlightHack = new RegExp(useRegExp ? text : text.escapeRegExp(),useCaseSensitive ? "mg" : "img");
	var matches = store.search(highlightHack,"title","excludeSearch");
	this.displayTiddlers(null,matches);
	highlightHack = null;
	var q = useRegExp ? "/" : "'";
	if(matches.length > 0)
		displayMessage(config.macros.search.successMsg.format([matches.length.toString(),q + text + q]));
	else
		displayMessage(config.macros.search.failureMsg.format([q + text + q]));
};

Story.prototype.findContainingTiddler = function(e)
{
	while(e && !hasClass(e,"tiddler")) {
		e = hasClass(e,"popup") && Popup.stack[0] ? Popup.stack[0].root : e.parentNode;
	}
	return e;
};

Story.prototype.gatherSaveFields = function(e,fields)
{
	if(e && e.getAttribute) {
		var f = e.getAttribute("edit");
		if(f)
			fields[f] = e.value.replace(/\r/mg,"");
		if(e.hasChildNodes()) {
			var c = e.childNodes;
			for(var t=0; t<c.length; t++)
				this.gatherSaveFields(c[t],fields);
		}
	}
};

Story.prototype.hasChanges = function(title)
{
	var e = this.getTiddler(title);
	if(e) {
		var fields = {};
		this.gatherSaveFields(e,fields);
		if(store.fetchTiddler(title)) {
			for(var n in fields) {
				if(store.getValue(title,n) != fields[n]) //# tiddler changed
					return true;
			}
		} else {
			if(store.isShadowTiddler(title) && store.getShadowTiddlerText(title) == fields.text) { //# not checking for title or tags
				return false;
			} else { //# changed shadow or new tiddler
				return true;
			}
		}
	}
	return false;
};

Story.prototype.saveTiddler = function(title,minorUpdate)
{
	var tiddlerElem = this.getTiddler(title);
	if(tiddlerElem) {
		var fields = {};
		this.gatherSaveFields(tiddlerElem,fields);
		var newTitle = fields.title || title;
		if(!store.tiddlerExists(newTitle)) {
			newTitle = newTitle.trim();
			var creator = config.options.txtUserName;
		}
		if(store.tiddlerExists(newTitle) && newTitle != title) {
			if(!confirm(config.messages.overwriteWarning.format([newTitle.toString()])))
				return null;
		}
		if(newTitle != title)
			this.closeTiddler(newTitle,false);
		tiddlerElem.id = this.tiddlerId(newTitle);
		tiddlerElem.setAttribute("tiddler",newTitle);
		tiddlerElem.setAttribute("template",DEFAULT_VIEW_TEMPLATE);
		tiddlerElem.setAttribute("dirty","false");
		if(config.options.chkForceMinorUpdate)
			minorUpdate = !minorUpdate;
		if(!store.tiddlerExists(newTitle))
			minorUpdate = false;
		var newDate = new Date();
		if(store.tiddlerExists(title)) {
			var t = store.fetchTiddler(title);
			var extendedFields = t.fields;
			creator = t.creator;
		} else {
			extendedFields = merge({},config.defaultCustomFields);
		}
		for(var n in fields) {
			if(!TiddlyWiki.isStandardField(n))
				extendedFields[n] = fields[n];
		}
		var tiddler = store.saveTiddler(title,newTitle,fields.text,minorUpdate ? undefined : config.options.txtUserName,minorUpdate ? undefined : newDate,fields.tags,extendedFields,null,null,creator);
		autoSaveChanges(null,[tiddler]);
		return newTitle;
	}
	return null;
};

Story.prototype.permaView = function()
{
	var links = [];
	this.forEachTiddler(function(title,element) {
		links.push(String.encodeTiddlyLink(title));
	});
	var t = encodeURIComponent(links.join(" "));
	if(t == "")
		t = "#";
	if(window.location.hash != t)
		window.location.hash = t;
};

Story.prototype.switchTheme = function(theme)
{
	if(safeMode)
		return;

	var isAvailable = function(title) {
		var s = title ? title.indexOf(config.textPrimitives.sectionSeparator) : -1;
		if(s!=-1)
			title = title.substr(0,s);
		return store.tiddlerExists(title) || store.isShadowTiddler(title);
	};

	var getSlice = function(theme,slice) {
		var r;
		if(readOnly)
			r = store.getTiddlerSlice(theme,slice+"ReadOnly") || store.getTiddlerSlice(theme,"Web"+slice);
		r = r || store.getTiddlerSlice(theme,slice);
		if(r && r.indexOf(config.textPrimitives.sectionSeparator)==0)
			r = theme + r;
		return isAvailable(r) ? r : slice;
	};

	var replaceNotification = function(i,name,theme,slice) {
		var newName = getSlice(theme,slice);
		if(name!=newName && store.namedNotifications[i].name==name) {
			store.namedNotifications[i].name = newName;
			return newName;
		}
		return name;
	};

	var pt = config.refresherData.pageTemplate;
	var vi = DEFAULT_VIEW_TEMPLATE;
	var vt = config.tiddlerTemplates[vi];
	var ei = DEFAULT_EDIT_TEMPLATE;
	var et = config.tiddlerTemplates[ei];

	for(var i=0; i<config.notifyTiddlers.length; i++) {
		var name = config.notifyTiddlers[i].name;
		switch(name) {
		case "PageTemplate":
			config.refresherData.pageTemplate = replaceNotification(i,config.refresherData.pageTemplate,theme,name);
			break;
		case "StyleSheet":
			removeStyleSheet(config.refresherData.styleSheet);
			config.refresherData.styleSheet = replaceNotification(i,config.refresherData.styleSheet,theme,name);
			break;
		case "ColorPalette":
			config.refresherData.colorPalette = replaceNotification(i,config.refresherData.colorPalette,theme,name);
			break;
		default:
			break;
		}
	}
	config.tiddlerTemplates[vi] = getSlice(theme,"ViewTemplate");
	config.tiddlerTemplates[ei] = getSlice(theme,"EditTemplate");
	if(!startingUp) {
		if(config.refresherData.pageTemplate!=pt || config.tiddlerTemplates[vi]!=vt || config.tiddlerTemplates[ei]!=et) {
			refreshAll();
			this.refreshAllTiddlers(true);
		} else {
			setStylesheet(store.getRecursiveTiddlerText(config.refresherData.styleSheet,"",10),config.refreshers.styleSheet);
		}
		config.options.txtTheme = theme;
		saveOption("txtTheme");
	}
};

//--
//-- Backstage
//--

var backstage = {
	area: null,
	toolbar: null,
	button: null,
	showButton: null,
	hideButton: null,
	cloak: null,
	panel: null,
	panelBody: null,
	panelFooter: null,
	currTabName: null,
	currTabElem: null,
	content: null,

	init: function() {
		var cmb = config.messages.backstage;
		this.area = document.getElementById("backstageArea");
		this.toolbar = jQuery("#backstageToolbar").empty()[0];
		this.button = jQuery("#backstageButton").empty()[0];
		this.button.style.display = "block";
		var t = cmb.open.text + " " + glyph("bentArrowLeft");
		this.showButton = createTiddlyButton(this.button,t,cmb.open.tooltip,
						function(e) {backstage.show(); return false;},null,"backstageShow");
		t = glyph("bentArrowRight") + " " + cmb.close.text;
		this.hideButton = createTiddlyButton(this.button,t,cmb.close.tooltip,
						function(e) {backstage.hide(); return false;},null,"backstageHide");
		this.cloak = document.getElementById("backstageCloak");
		this.panel = document.getElementById("backstagePanel");
		this.panelFooter = createTiddlyElement(this.panel,"div",null,"backstagePanelFooter");
		this.panelBody = createTiddlyElement(this.panel,"div",null,"backstagePanelBody");
		this.cloak.onmousedown = function(e) {backstage.switchTab(null);};
		createTiddlyText(this.toolbar,cmb.prompt);
		for(t=0; t<config.backstageTasks.length; t++) {
			var taskName = config.backstageTasks[t];
			var task = config.tasks[taskName];
			var handler = task.action ? this.onClickCommand : this.onClickTab;
			var text = task.text + (task.action ? "" : glyph("downTriangle"));
			var btn = createTiddlyButton(this.toolbar,text,task.tooltip,handler,"backstageTab");
			addClass(btn,task.action ? "backstageAction" : "backstageTask");
			btn.setAttribute("task", taskName);
			}
		this.content = document.getElementById("contentWrapper");
		if(config.options.chkBackstage)
			this.show();
		else
			this.hide();
	},

	isVisible: function() {
		return this.area ? this.area.style.display == "block" : false;
	},

	show: function() {
		this.area.style.display = "block";
		if(anim && config.options.chkAnimate) {
			backstage.toolbar.style.left = findWindowWidth() + "px";
			var p = [{style: "left", start: findWindowWidth(), end: 0, template: "%0px"}];
			anim.startAnimating(new Morpher(backstage.toolbar,config.animDuration,p));
		} else {
			backstage.area.style.left = "0px";
		}
		jQuery(this.showButton).hide();
		jQuery(this.hideButton).show();
		config.options.chkBackstage = true;
		saveOption("chkBackstage");
		addClass(this.content,"backstageVisible");
	},

	hide: function() {
		if(this.currTabElem) {
			this.switchTab(null);
		} else {
			backstage.toolbar.style.left = "0px";
			if(anim && config.options.chkAnimate) {
				var p = [{style: "left", start: 0, end: findWindowWidth(), template: "%0px"}];
				var c = function(element,properties) {backstage.area.style.display = "none";};
				anim.startAnimating(new Morpher(backstage.toolbar,config.animDuration,p,c));
			} else {
				this.area.style.display = "none";
			}
			this.showButton.style.display = "block";
			this.hideButton.style.display = "none";
			config.options.chkBackstage = false;
			saveOption("chkBackstage");
			removeClass(this.content, "backstageVisible");
		}
	},

	onClickCommand: function(e) {
		var task = config.tasks[this.getAttribute("task")];
		if(task.action) {
			backstage.switchTab(null);
			task.action();
		}
		return false;
	},

	onClickTab: function(e) {
		backstage.switchTab(this.getAttribute("task"));
		return false;
	},

	// Switch to a given tab, or none if null is passed
	switchTab: function(tabName) {
		var tabElem = null;
		var e = this.toolbar.firstChild;
		while(e)
			{
			if(e.getAttribute && e.getAttribute("task") == tabName)
				tabElem = e;
			e = e.nextSibling;
			}
		if(tabName == backstage.currTabName) {
			backstage.hidePanel();
			return;
		}
		if(backstage.currTabElem) {
			removeClass(this.currTabElem, "backstageSelTab");
		}
		if(tabElem && tabName) {
			backstage.preparePanel();
			addClass(tabElem,"backstageSelTab");
			var task = config.tasks[tabName];
			wikify(task.content,backstage.panelBody,null,null);
			backstage.showPanel();
		} else if(backstage.currTabElem) {
			backstage.hidePanel();
		}
		backstage.currTabName = tabName;
		backstage.currTabElem = tabElem;
	},

	isPanelVisible: function() {
		return backstage.panel ? backstage.panel.style.display == "block" : false;
	},

	preparePanel: function() {
		backstage.cloak.style.height = findWindowHeight() + "px";
		backstage.cloak.style.display = "block";
		removeChildren(backstage.panelBody);
		return backstage.panelBody;
	},

	showPanel: function() {
		backstage.panel.style.display = "block";
		if(anim && config.options.chkAnimate) {
			backstage.panel.style.top = (-backstage.panel.offsetHeight) + "px";
			var p = [{style: "top", start: -backstage.panel.offsetHeight, end: 0, template: "%0px"}];
			anim.startAnimating(new Morpher(backstage.panel,config.animDuration,p),new Scroller(backstage.panel,false));
		} else {
			backstage.panel.style.top = "0px";
		}
		return backstage.panelBody;
	},

	hidePanel: function() {
		if(backstage.currTabElem)
			removeClass(backstage.currTabElem, "backstageSelTab");
		backstage.currTabElem = null;
		backstage.currTabName = null;
		if(anim && config.options.chkAnimate) {
			var p = [
				{style: "top", start: 0, end: -(backstage.panel.offsetHeight), template: "%0px"},
				{style: "display", atEnd: "none"}
			];
			var c = function(element,properties) {backstage.cloak.style.display = "none";};
			anim.startAnimating(new Morpher(backstage.panel,config.animDuration,p,c));
		} else {
			jQuery([backstage.panel,backstage.cloak]).hide();
		}
	}
};

config.macros.backstage = {};

config.macros.backstage.handler = function(place,macroName,params)
{
	var backstageTask = config.tasks[params[0]];
	if(backstageTask)
		createTiddlyButton(place,backstageTask.text,backstageTask.tooltip,function(e) {backstage.switchTab(params[0]); return false;});
};

//--
//-- ImportTiddlers macro
//--

config.macros.importTiddlers.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
	if(readOnly) {
		createTiddlyElement(place,"div",null,"marked",this.readOnlyWarning);
		return;
	}
	var w = new Wizard();
	w.createWizard(place,this.wizardTitle);
	this.restart(w);
};

config.macros.importTiddlers.onCancel = function(e)
{
	var wizard = new Wizard(this);
	var place = wizard.clear();
	config.macros.importTiddlers.restart(wizard);
	return false;
};

config.macros.importTiddlers.onClose = function(e)
{
	backstage.hidePanel();
	return false;
};

config.macros.importTiddlers.restart = function(wizard)
{
	var me = config.macros.importTiddlers;
	wizard.addStep(this.step1Title,this.step1Html);
	var s = wizard.getElement("selTypes");
	for(var t in config.adaptors) {
		var e = createTiddlyElement(s,"option",null,null,config.adaptors[t].serverLabel ? config.adaptors[t].serverLabel : t);
		e.value = t;
	}
	if(config.defaultAdaptor)
		s.value = config.defaultAdaptor;
	s = wizard.getElement("selFeeds");
	var feeds = this.getFeeds();
	for(t in feeds) {
		e = createTiddlyElement(s,"option",null,null,t);
		e.value = t;
	}
	wizard.setValue("feeds",feeds);
	s.onchange = me.onFeedChange;
	var fileInput = wizard.getElement("txtBrowse");
	fileInput.onchange = me.onBrowseChange;
	fileInput.onkeyup = me.onBrowseChange;
	wizard.setButtons([{caption: this.openLabel, tooltip: this.openPrompt, onClick: me.onOpen}]);
	wizard.formElem.action = "javascript:;";
	wizard.formElem.onsubmit = function() {
		if(!this.txtPath || this.txtPath.value.length) //# check for manually entered path in first step
			this.lastChild.firstChild.onclick();
	};
};

config.macros.importTiddlers.getFeeds = function()
{
	var feeds = {};
	var tagged = store.getTaggedTiddlers("systemServer","title");
	for(var t=0; t<tagged.length; t++) {
		var title = tagged[t].title;
		var serverType = store.getTiddlerSlice(title,"Type");
		if(!serverType)
			serverType = "file";
		feeds[title] = {title: title,
						url: store.getTiddlerSlice(title,"URL"),
						workspace: store.getTiddlerSlice(title,"Workspace"),
						workspaceList: store.getTiddlerSlice(title,"WorkspaceList"),
						tiddlerFilter: store.getTiddlerSlice(title,"TiddlerFilter"),
						serverType: serverType,
						description: store.getTiddlerSlice(title,"Description")};
	}
	return feeds;
};

config.macros.importTiddlers.onFeedChange = function(e)
{
	var wizard = new Wizard(this);
	var selTypes = wizard.getElement("selTypes");
	var fileInput = wizard.getElement("txtPath");
	var feeds = wizard.getValue("feeds");
	var f = feeds[this.value];
	if(f) {
		selTypes.value = f.serverType;
		fileInput.value = f.url;
		wizard.setValue("feedName",f.serverType);
		wizard.setValue("feedHost",f.url);
		wizard.setValue("feedWorkspace",f.workspace);
		wizard.setValue("feedWorkspaceList",f.workspaceList);
		wizard.setValue("feedTiddlerFilter",f.tiddlerFilter);
	}
	return false;
};

config.macros.importTiddlers.onBrowseChange = function(e)
{
	var wizard = new Wizard(this);
	if(this.files && this.files[0]) {
		try {
			netscape.security.PrivilegeManager.enablePrivilege("UniversalFileRead");
		} catch (ex) {
			showException(ex);
		}
	}
	var fileInput = wizard.getElement("txtPath");
	fileInput.value = config.macros.importTiddlers.getURLFromLocalPath(this.value);
	var serverType = wizard.getElement("selTypes");
	serverType.value = "file";
	return true;
};

config.macros.importTiddlers.getURLFromLocalPath = function(v)
{
	if(!v || !v.length)
		return v;
	v = v.replace(/\\/g,"/"); // use "/" for cross-platform consistency
	var u;
	var t = v.split(":");
	var p = t[1] || t[0]; // remove drive letter (if any)
	if(t[1] && (t[0] == "http" || t[0] == "https" || t[0] == "file")) {
		u = v;
	} else if(p.substr(0,1)=="/") {
		u = document.location.protocol + "//" + document.location.hostname + (t[1] ? "/" : "") + v;
	} else {
		var c = document.location.href.replace(/\\/g,"/");
		var pos = c.lastIndexOf("/");
		if(pos!=-1)
			c = c.substr(0,pos); // remove filename
		u = c + "/" + p;
	}
	return u;
};

config.macros.importTiddlers.onOpen = function(e)
{
	var me = config.macros.importTiddlers;
	var wizard = new Wizard(this);
	var fileInput = wizard.getElement("txtPath");
	var url = fileInput.value;
	var serverType = wizard.getElement("selTypes").value || config.defaultAdaptor;
	var adaptor = new config.adaptors[serverType]();
	wizard.setValue("adaptor",adaptor);
	wizard.setValue("serverType",serverType);
	wizard.setValue("host",url);
	var ret = adaptor.openHost(url,null,wizard,me.onOpenHost);
	if(ret !== true)
		displayMessage(ret);
	wizard.setButtons([{caption: me.cancelLabel, tooltip: me.cancelPrompt, onClick: me.onCancel}],me.statusOpenHost);
	return false;
};

config.macros.importTiddlers.onOpenHost = function(context,wizard)
{
	var me = config.macros.importTiddlers;
	var adaptor = wizard.getValue("adaptor");
	if(context.status !== true)
		displayMessage("Error in importTiddlers.onOpenHost: " + context.statusText);
	var ret = adaptor.getWorkspaceList(context,wizard,me.onGetWorkspaceList);
	if(ret !== true)
		displayMessage(ret);
	wizard.setButtons([{caption: me.cancelLabel, tooltip: me.cancelPrompt, onClick: me.onCancel}],me.statusGetWorkspaceList);
};

config.macros.importTiddlers.onGetWorkspaceList = function(context,wizard)
{
	var me = config.macros.importTiddlers;
	if(context.status !== true)
		displayMessage("Error in importTiddlers.onGetWorkspaceList: " + context.statusText);
	wizard.setValue("context",context);
	var workspace = wizard.getValue("feedWorkspace");
	if(!workspace && context.workspaces.length==1)
		workspace = context.workspaces[0].title;
	if(workspace) {
		var ret = context.adaptor.openWorkspace(workspace,context,wizard,me.onOpenWorkspace);
		if(ret !== true)
			displayMessage(ret);
		wizard.setValue("workspace",workspace);
		wizard.setButtons([{caption: me.cancelLabel, tooltip: me.cancelPrompt, onClick: me.onCancel}],me.statusOpenWorkspace);
		return;
	}
	wizard.addStep(me.step2Title,me.step2Html);
	var s = wizard.getElement("selWorkspace");
	s.onchange = me.onWorkspaceChange;
	for(var t=0; t<context.workspaces.length; t++) {
		var e = createTiddlyElement(s,"option",null,null,context.workspaces[t].title);
		e.value = context.workspaces[t].title;
	}
	var workspaceList = wizard.getValue("feedWorkspaceList");
	if(workspaceList) {
		var list = workspaceList.parseParams("workspace",null,false,true);
		for(var n=1; n<list.length; n++) {
			if(context.workspaces.findByField("title",list[n].value) == null) {
				e = createTiddlyElement(s,"option",null,null,list[n].value);
				e.value = list[n].value;
			}
		}
	}
	if(workspace) {
		t = wizard.getElement("txtWorkspace");
		t.value = workspace;
	}
	wizard.setButtons([{caption: me.openLabel, tooltip: me.openPrompt, onClick: me.onChooseWorkspace}]);
};

config.macros.importTiddlers.onWorkspaceChange = function(e)
{
	var wizard = new Wizard(this);
	var t = wizard.getElement("txtWorkspace");
	t.value = this.value;
	this.selectedIndex = 0;
	return false;
};

config.macros.importTiddlers.onChooseWorkspace = function(e)
{
	var me = config.macros.importTiddlers;
	var wizard = new Wizard(this);
	var adaptor = wizard.getValue("adaptor");
	var workspace = wizard.getElement("txtWorkspace").value;
	wizard.setValue("workspace",workspace);
	var context = wizard.getValue("context");
	var ret = adaptor.openWorkspace(workspace,context,wizard,me.onOpenWorkspace);
	if(ret !== true)
		displayMessage(ret);
	wizard.setButtons([{caption: me.cancelLabel, tooltip: me.cancelPrompt, onClick: me.onCancel}],me.statusOpenWorkspace);
	return false;
};

config.macros.importTiddlers.onOpenWorkspace = function(context,wizard)
{
	var me = config.macros.importTiddlers;
	if(context.status !== true)
		displayMessage("Error in importTiddlers.onOpenWorkspace: " + context.statusText);
	var adaptor = wizard.getValue("adaptor");
	var ret = adaptor.getTiddlerList(context,wizard,me.onGetTiddlerList,wizard.getValue("feedTiddlerFilter"));
	if(ret !== true)
		displayMessage(ret);
	wizard.setButtons([{caption: me.cancelLabel, tooltip: me.cancelPrompt, onClick: me.onCancel}],me.statusGetTiddlerList);
};

config.macros.importTiddlers.onGetTiddlerList = function(context,wizard)
{
	var me = config.macros.importTiddlers;
	if(context.status !== true) {
		wizard.setButtons([{caption: me.cancelLabel, tooltip: me.cancelPrompt, onClick: me.onCancel}],me.errorGettingTiddlerList);
		return;
	}
	// Extract data for the listview
	var listedTiddlers = [];
	if(context.tiddlers) {
		for(var n=0; n<context.tiddlers.length; n++) {
			var tiddler = context.tiddlers[n];
			listedTiddlers.push({
				title: tiddler.title,
				modified: tiddler.modified,
				modifier: tiddler.modifier,
				text: tiddler.text ? wikifyPlainText(tiddler.text,100) : "",
				tags: tiddler.tags,
				size: tiddler.text ? tiddler.text.length : 0,
				tiddler: tiddler
			});
		}
	}
	listedTiddlers.sort(function(a,b) {return a.title < b.title ? -1 : (a.title == b.title ? 0 : +1);});
	// Display the listview
	wizard.addStep(me.step3Title,me.step3Html);
	var markList = wizard.getElement("markList");
	var listWrapper = document.createElement("div");
	markList.parentNode.insertBefore(listWrapper,markList);
	var listView = ListView.create(listWrapper,listedTiddlers,me.listViewTemplate);
	wizard.setValue("listView",listView);
	wizard.setValue("context",context);
	var txtSaveTiddler = wizard.getElement("txtSaveTiddler");
	txtSaveTiddler.value = me.generateSystemServerName(wizard);
	wizard.setButtons([
			{caption: me.cancelLabel, tooltip: me.cancelPrompt, onClick: me.onCancel},
			{caption: me.importLabel, tooltip: me.importPrompt, onClick: me.doImport}
		]);
};

config.macros.importTiddlers.generateSystemServerName = function(wizard)
{
	var serverType = wizard.getValue("serverType");
	var host = wizard.getValue("host");
	var workspace = wizard.getValue("workspace");
	var pattern = config.macros.importTiddlers[workspace ? "systemServerNamePattern" : "systemServerNamePatternNoWorkspace"];
	return pattern.format([serverType,host,workspace]);
};

config.macros.importTiddlers.saveServerTiddler = function(wizard)
{
	var me = config.macros.importTiddlers;
	var txtSaveTiddler = wizard.getElement("txtSaveTiddler").value;
	if(store.tiddlerExists(txtSaveTiddler)) {
		if(!confirm(me.confirmOverwriteSaveTiddler.format([txtSaveTiddler])))
			return;
		store.suspendNotifications();
		store.removeTiddler(txtSaveTiddler);
		store.resumeNotifications();
	}
	var serverType = wizard.getValue("serverType");
	var host = wizard.getValue("host");
	var workspace = wizard.getValue("workspace");
	var text = me.serverSaveTemplate.format([serverType,host,workspace]);
	store.saveTiddler(txtSaveTiddler,txtSaveTiddler,text,me.serverSaveModifier,new Date(),["systemServer"]);
};

config.macros.importTiddlers.doImport = function(e)
{
	var me = config.macros.importTiddlers;
	var wizard = new Wizard(this);
	if(wizard.getElement("chkSave").checked)
		me.saveServerTiddler(wizard);
	var chkSync = wizard.getElement("chkSync").checked;
	wizard.setValue("sync",chkSync);
	var listView = wizard.getValue("listView");
	var rowNames = ListView.getSelectedRows(listView);
	var adaptor = wizard.getValue("adaptor");
	var overwrite = [];
	var t;
	for(t=0; t<rowNames.length; t++) {
		if(store.tiddlerExists(rowNames[t]))
			overwrite.push(rowNames[t]);
	}
	if(overwrite.length > 0) {
		if(!confirm(me.confirmOverwriteText.format([overwrite.join(", ")])))
			return false;
	}
	wizard.addStep(me.step4Title.format([rowNames.length]),me.step4Html);
	for(t=0; t<rowNames.length; t++) {
		var link = document.createElement("div");
		createTiddlyLink(link,rowNames[t],true);
		var place = wizard.getElement("markReport");
		place.parentNode.insertBefore(link,place);
	}
	wizard.setValue("remainingImports",rowNames.length);
	wizard.setButtons([
			{caption: me.cancelLabel, tooltip: me.cancelPrompt, onClick: me.onCancel}
		],me.statusDoingImport);
	var wizardContext = wizard.getValue("context");
	var tiddlers = wizardContext ? wizardContext.tiddlers : [];
	for(t=0; t<rowNames.length; t++) {
		var context = {
			allowSynchronous:true,
			tiddler:tiddlers[tiddlers.findByField("title",rowNames[t])]
		};
		adaptor.getTiddler(rowNames[t],context,wizard,me.onGetTiddler);
	}
	return false;
};

config.macros.importTiddlers.onGetTiddler = function(context,wizard)
{
	var me = config.macros.importTiddlers;
	if(!context.status)
		displayMessage("Error in importTiddlers.onGetTiddler: " + context.statusText);
	var tiddler = context.tiddler;
	store.suspendNotifications();
	store.saveTiddler(tiddler.title, tiddler.title, tiddler.text, tiddler.modifier, tiddler.modified, tiddler.tags, tiddler.fields, true, tiddler.created);
	if(!wizard.getValue("sync")) {
		store.setValue(tiddler.title,'server',null);
	}
	store.resumeNotifications();
	if(!context.isSynchronous)
		store.notify(tiddler.title,true);
	var remainingImports = wizard.getValue("remainingImports")-1;
	wizard.setValue("remainingImports",remainingImports);
	if(remainingImports == 0) {
		if(context.isSynchronous) {
			store.notifyAll();
			refreshDisplay();
		}
		wizard.setButtons([
				{caption: me.doneLabel, tooltip: me.donePrompt, onClick: me.onClose}
			],me.statusDoneImport);
		autoSaveChanges();
	}
};

//--
//-- Upgrade macro
//--

config.macros.upgrade.handler = function(place)
{
	var w = new Wizard();
	w.createWizard(place,this.wizardTitle);
	w.addStep(this.step1Title,this.step1Html.format([this.source,this.source]));
	w.setButtons([{caption: this.upgradeLabel, tooltip: this.upgradePrompt, onClick: this.onClickUpgrade}]);
};

config.macros.upgrade.onClickUpgrade = function(e)
{
	var me = config.macros.upgrade;
	var w = new Wizard(this);
	if(window.location.protocol != "file:") {
		alert(me.errorCantUpgrade);
		return false;
	}
	if(story.areAnyDirty() || store.isDirty()) {
		alert(me.errorNotSaved);
		return false;
	}
	var localPath = getLocalPath(document.location.toString());
	var backupPath = getBackupPath(localPath,me.backupExtension);
	w.setValue("backupPath",backupPath);
	w.setButtons([],me.statusPreparingBackup);
	var original = loadOriginal(localPath);
	w.setButtons([],me.statusSavingBackup);
	var backup = copyFile(backupPath,localPath);
	if(!backup)
		backup = saveFile(backupPath,original);
	if(!backup) {
		w.setButtons([],me.errorSavingBackup);
		alert(me.errorSavingBackup);
		return false;
	}
	w.setButtons([],me.statusLoadingCore);
	var load = loadRemoteFile(me.source,me.onLoadCore,w);
	if(typeof load == "string") {
		w.setButtons([],me.errorLoadingCore);
		alert(me.errorLoadingCore);
		return false;
	}
	return false;
};

config.macros.upgrade.onLoadCore = function(status,params,responseText,url,xhr)
{
	var me = config.macros.upgrade;
	var w = params;
	var errMsg;
	if(!status)
		errMsg = me.errorLoadingCore;
	var newVer = me.extractVersion(responseText);
	if(!newVer)
		errMsg = me.errorCoreFormat;
	if(errMsg) {
		w.setButtons([],errMsg);
		alert(errMsg);
		return;
	}
	var onStartUpgrade = function(e) {
		w.setButtons([],me.statusSavingCore);
		var localPath = getLocalPath(document.location.toString());
		saveFile(localPath,responseText);
		w.setButtons([],me.statusReloadingCore);
		var backupPath = w.getValue("backupPath");
		var newLoc = document.location.toString() + "?time=" + new Date().convertToYYYYMMDDHHMM() + "#upgrade:[[" + encodeURI(backupPath) + "]]";
		window.setTimeout(function () {window.location = newLoc;},10);
	};
	var step2 = [me.step2Html_downgrade,me.step2Html_restore,me.step2Html_upgrade][compareVersions(version,newVer) + 1];
	w.addStep(me.step2Title,step2.format([formatVersion(newVer),formatVersion(version)]));
	w.setButtons([{caption: me.startLabel, tooltip: me.startPrompt, onClick: onStartUpgrade},{caption: me.cancelLabel, tooltip: me.cancelPrompt, onClick: me.onCancel}]);
};

config.macros.upgrade.onCancel = function(e)
{
	var me = config.macros.upgrade;
	var w = new Wizard(this);
	w.addStep(me.step3Title,me.step3Html);
	w.setButtons([]);
	return false;
};

config.macros.upgrade.extractVersion = function(upgradeFile)
{
	var re = /^var version = \{title: "([^"]+)", major: (\d+), minor: (\d+), revision: (\d+)(, beta: (\d+)){0,1}, date: new Date\("([^"]+)"\)/mg;
	var m = re.exec(upgradeFile);
	return m ? {title: m[1], major: m[2], minor: m[3], revision: m[4], beta: m[6], date: new Date(m[7])} : null;
};

function upgradeFrom(path)
{
	var importStore = new TiddlyWiki();
	var tw = loadFile(path);
	if(window.netscape !== undefined)
		tw = convertUTF8ToUnicode(tw);
	importStore.importTiddlyWiki(tw);
	importStore.forEachTiddler(function(title,tiddler) {
		if(!store.getTiddler(title)) {
			store.addTiddler(tiddler);
		}
	});
	refreshDisplay();
	saveChanges(); //# To create appropriate Markup* sections
	alert(config.messages.upgradeDone.format([formatVersion()]));
	window.location = window.location.toString().substr(0,window.location.toString().lastIndexOf("?"));
}

//--
//-- Sync macro
//--

// Synchronisation handlers
config.syncers = {};

// Sync state.
var currSync = null;

// sync macro
config.macros.sync.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
	if(!wikifier.isStatic)
		this.startSync(place);
};

config.macros.sync.cancelSync = function()
{
	currSync = null;
};

config.macros.sync.startSync = function(place)
{
	if(currSync)
		config.macros.sync.cancelSync();
	currSync = {};
	currSync.syncList = this.getSyncableTiddlers();
	currSync.syncTasks = this.createSyncTasks(currSync.syncList);
	this.preProcessSyncableTiddlers(currSync.syncList);
	var wizard = new Wizard();
	currSync.wizard = wizard;
	wizard.createWizard(place,this.wizardTitle);
	wizard.addStep(this.step1Title,this.step1Html);
	var markList = wizard.getElement("markList");
	var listWrapper = document.createElement("div");
	markList.parentNode.insertBefore(listWrapper,markList);
	currSync.listView = ListView.create(listWrapper,currSync.syncList,this.listViewTemplate);
	this.processSyncableTiddlers(currSync.syncList);
	wizard.setButtons([{caption: this.syncLabel, tooltip: this.syncPrompt, onClick: this.doSync}]);
};

config.macros.sync.getSyncableTiddlers = function()
{
	var list = [];
	store.forEachTiddler(function(title,tiddler) {
		var syncItem = {};
		syncItem.serverType = tiddler.getServerType();
		syncItem.serverHost = tiddler.fields['server.host'];
		if(syncItem.serverType && syncItem.serverHost) {
			syncItem.adaptor = new config.adaptors[syncItem.serverType];
			syncItem.serverHost = syncItem.adaptor.fullHostName(syncItem.serverHost);
			syncItem.serverWorkspace = tiddler.fields['server.workspace'];
			syncItem.tiddler = tiddler;
			syncItem.title = tiddler.title;
			syncItem.isTouched = tiddler.isTouched();
			syncItem.selected = syncItem.isTouched;
			syncItem.syncStatus = config.macros.sync.syncStatusList[syncItem.isTouched ? "changedLocally" : "none"];
			syncItem.status = syncItem.syncStatus.text;
			list.push(syncItem);
		}
		});
	list.sort(function(a,b) {return a.title < b.title ? -1 : (a.title == b.title ? 0 : +1);});
	return list;
};

config.macros.sync.preProcessSyncableTiddlers = function(syncList)
{
	for(var i=0; i<syncList.length; i++) {
		var si = syncList[i];
		si.serverUrl = si.adaptor.generateTiddlerInfo(si.tiddler).uri;
	}
};

config.macros.sync.processSyncableTiddlers = function(syncList)
{
	for(var i=0; i<syncList.length; i++) {
		var si = syncList[i];
		if(si.syncStatus.display)
			si.rowElement.style.display = si.syncStatus.display;
		if(si.syncStatus.className)
			si.rowElement.className = si.syncStatus.className;
	}
};

config.macros.sync.createSyncTasks = function(syncList)
{
	var syncTasks = [];
	for(var i=0; i<syncList.length; i++) {
		var si = syncList[i];
		var r = null;
		for(var j=0; j<syncTasks.length; j++) {
			var cst = syncTasks[j];
			if(si.serverType == cst.serverType && si.serverHost == cst.serverHost && si.serverWorkspace == cst.serverWorkspace)
				r = cst;
		}
		if(r) {
			si.syncTask = r;
			r.syncItems.push(si);
		} else {
			si.syncTask = this.createSyncTask(si);
			syncTasks.push(si.syncTask);
		}
	}
	return syncTasks;
};

config.macros.sync.createSyncTask = function(syncItem)
{
	var st = {};
	st.serverType = syncItem.serverType;
	st.serverHost = syncItem.serverHost;
	st.serverWorkspace = syncItem.serverWorkspace;
	st.syncItems = [syncItem];

	var openWorkspaceCallback = function(context,syncItems) {
		if(context.status) {
			context.adaptor.getTiddlerList(context,syncItems,getTiddlerListCallback);
			return true;
		}
		displayMessage(context.statusText);
		return false;
	};

	var getTiddlerListCallback = function(context,sycnItems) {
		var me = config.macros.sync;
		if(!context.status) {
			displayMessage(context.statusText);
			return false;
		}
		syncItems = context.userParams;
		var tiddlers = context.tiddlers;
		for(var i=0; i<syncItems.length; i++) {
			var si = syncItems[i];
			var f = tiddlers.findByField("title",si.title);
			if(f !== null) {
				if(tiddlers[f].fields['server.page.revision'] > si.tiddler.fields['server.page.revision']) {
					si.syncStatus = me.syncStatusList[si.isTouched ? 'changedBoth' : 'changedServer'];
				}
			} else {
				si.syncStatus = me.syncStatusList.notFound;
			}
			me.updateSyncStatus(si);
		}
		return true;
	};
	var context = {host:st.serverHost,workspace:st.serverWorkspace};
	syncItem.adaptor.openHost(st.serverHost);
	syncItem.adaptor.openWorkspace(st.serverWorkspace,context,st.syncItems,openWorkspaceCallback);
	return st;
};

config.macros.sync.updateSyncStatus = function(syncItem)
{
	var e = syncItem.colElements["status"];
	removeChildren(e);
	createTiddlyText(e,syncItem.syncStatus.text);
	syncItem.rowElement.style.display = syncItem.syncStatus.display;
	if(syncItem.syncStatus.className)
		syncItem.rowElement.className = syncItem.syncStatus.className;
};

config.macros.sync.doSync = function(e)
{
	var me = config.macros.sync;
	var getTiddlerCallback = function(context,syncItem) {
		if(syncItem) {
			var tiddler = context.tiddler;
			store.saveTiddler(tiddler.title,tiddler.title,tiddler.text,tiddler.modifier,tiddler.modified,tiddler.tags,tiddler.fields,true,tiddler.created);
			syncItem.syncStatus = me.syncStatusList.gotFromServer;
			me.updateSyncStatus(syncItem);
		}
	};
	var putTiddlerCallback = function(context,syncItem) {
		if(syncItem) {
			store.resetTiddler(context.title);
			syncItem.syncStatus = me.syncStatusList.putToServer;
			me.updateSyncStatus(syncItem);
		}
	};

	var rowNames = ListView.getSelectedRows(currSync.listView);
	var sl = me.syncStatusList;
	for(var i=0; i<currSync.syncList.length; i++) {
		var si = currSync.syncList[i];
		if(rowNames.indexOf(si.title) != -1) {
			var errorMsg = "Error in doSync: ";
			try {
				var r = true;
				switch(si.syncStatus) {
				case sl.changedServer:
					var context = {"workspace": si.serverWorkspace};
					r = si.adaptor.getTiddler(si.title,context,si,getTiddlerCallback);
					break;
				case sl.notFound:
				case sl.changedLocally:
				case sl.changedBoth:
					r = si.adaptor.putTiddler(si.tiddler,null,si,putTiddlerCallback);
					break;
				default:
					break;
				}
				if(!r)
					displayMessage(errorMsg + r);
			} catch(ex) {
				if(ex.name == "TypeError")
					displayMessage("sync operation unsupported: " + ex.message);
				else
					displayMessage(errorMsg + ex.message);
			}
		}
	}
	return false;
};

//--
//-- Manager UI for groups of tiddlers
//--

config.macros.plugins.handler = function(place,macroName,params,wikifier,paramString)
{
	var wizard = new Wizard();
	wizard.createWizard(place,this.wizardTitle);
	wizard.addStep(this.step1Title,this.step1Html);
	var markList = wizard.getElement("markList");
	var listWrapper = document.createElement("div");
	markList.parentNode.insertBefore(listWrapper,markList);
	listWrapper.setAttribute("refresh","macro");
	listWrapper.setAttribute("macroName","plugins");
	listWrapper.setAttribute("params",paramString);
	this.refresh(listWrapper,paramString);
};

config.macros.plugins.refresh = function(listWrapper,params)
{
	var me = config.macros.plugins;
	var wizard = new Wizard(listWrapper);
	var selectedRows = [];
	ListView.forEachSelector(listWrapper,function(e,rowName) {
			if(e.checked)
				selectedRows.push(e.getAttribute("rowName"));
		});
	removeChildren(listWrapper);
	params = params.parseParams("anon");
	var plugins = installedPlugins.slice(0);
	var t,tiddler,p;
	var configTiddlers = store.getTaggedTiddlers("systemConfig");
	for(t=0; t<configTiddlers.length; t++) {
		tiddler = configTiddlers[t];
		if(plugins.findByField("title",tiddler.title) == null) {
			p = getPluginInfo(tiddler);
			p.executed = false;
			p.log.splice(0,0,this.skippedText);
			plugins.push(p);
		}
	}
	for(t=0; t<plugins.length; t++) {
		p = plugins[t];
		p.size = p.tiddler.text ? p.tiddler.text.length : 0;
		p.forced = p.tiddler.isTagged("systemConfigForce");
		p.disabled = p.tiddler.isTagged("systemConfigDisable");
		p.Selected = selectedRows.indexOf(plugins[t].title) != -1;
	}
	if(plugins.length == 0) {
		createTiddlyElement(listWrapper,"em",null,null,this.noPluginText);
		wizard.setButtons([]);
	} else {
		var template = readOnly ? this.listViewTemplateReadOnly : this.listViewTemplate;
		var listView = ListView.create(listWrapper,plugins,template,this.onSelectCommand);
		wizard.setValue("listView",listView);
		if(!readOnly) {
			wizard.setButtons([
				{caption: me.removeLabel, tooltip: me.removePrompt, onClick: me.doRemoveTag},
				{caption: me.deleteLabel, tooltip: me.deletePrompt, onClick: me.doDelete}
			]);
		}
	}
};

config.macros.plugins.doRemoveTag = function(e)
{
	var wizard = new Wizard(this);
	var listView = wizard.getValue("listView");
	var rowNames = ListView.getSelectedRows(listView);
	if(rowNames.length == 0) {
		alert(config.messages.nothingSelected);
	} else {
		for(var t=0; t<rowNames.length; t++)
			store.setTiddlerTag(rowNames[t],false,"systemConfig");
	}
};

config.macros.plugins.doDelete = function(e)
{
	var wizard = new Wizard(this);
	var listView = wizard.getValue("listView");
	var rowNames = ListView.getSelectedRows(listView);
	if(rowNames.length == 0) {
		alert(config.messages.nothingSelected);
	} else {
		if(confirm(config.macros.plugins.confirmDeleteText.format([rowNames.join(", ")]))) {
			for(var t=0; t<rowNames.length; t++) {
				store.removeTiddler(rowNames[t]);
				story.closeTiddler(rowNames[t],true);
			}
		}
	}
};

//--
//-- Message area
//--

function getMessageDiv()
{
	var msgArea = document.getElementById("messageArea");
	if(!msgArea)
		return null;
	if(!msgArea.hasChildNodes())
		createTiddlyButton(createTiddlyElement(msgArea,"div",null,"messageToolbar"),
			config.messages.messageClose.text,
			config.messages.messageClose.tooltip,
			clearMessage);
	msgArea.style.display = "block";
	return createTiddlyElement(msgArea,"div");
}

function displayMessage(text,linkText)
{
	var e = getMessageDiv();
	if(!e) {
		alert(text);
		return;
	}
	if(linkText) {
		var link = createTiddlyElement(e,"a",null,null,text);
		link.href = linkText;
		link.target = "_blank";
	} else {
		e.appendChild(document.createTextNode(text));
	}
}

function clearMessage()
{
	var msgArea = document.getElementById("messageArea");
	if(msgArea) {
		removeChildren(msgArea);
		msgArea.style.display = "none";
	}
	return false;
}

//--
//-- Refresh mechanism
//--

config.notifyTiddlers = [
	{name: "SystemSettings", notify: onSystemSettingsChange},
	{name: "StyleSheetLayout", notify: refreshStyles},
	{name: "StyleSheetColors", notify: refreshStyles},
	{name: "StyleSheet", notify: refreshStyles},
	{name: "StyleSheetPrint", notify: refreshStyles},
	{name: "PageTemplate", notify: refreshPageTemplate},
	{name: "SiteTitle", notify: refreshPageTitle},
	{name: "SiteSubtitle", notify: refreshPageTitle},
	{name: "WindowTitle", notify: refreshPageTitle},
	{name: "ColorPalette", notify: refreshColorPalette},
	{name: null, notify: refreshDisplay}
];

config.refreshers = {
	link: function(e,changeList)
		{
		var title = e.getAttribute("tiddlyLink");
		refreshTiddlyLink(e,title);
		return true;
		},

	tiddler: function(e,changeList)
		{
		var title = e.getAttribute("tiddler");
		var template = e.getAttribute("template");
		if(changeList && (changeList.indexOf && changeList.indexOf(title) != -1) && !story.isDirty(title))
			story.refreshTiddler(title,template,true);
		else
			refreshElements(e,changeList);
		return true;
		},

	content: function(e,changeList)
		{
		var title = e.getAttribute("tiddler");
		var force = e.getAttribute("force");
		var args = e.getAttribute("args");
		if(force != null || changeList == null || (changeList.indexOf && changeList.indexOf(title) != -1)) {
			removeChildren(e);
			config.macros.tiddler.transclude(e,title,args);
			return true;
		} else
			return false;
		},

	macro: function(e,changeList)
		{
		var macro = e.getAttribute("macroName");
		var params = e.getAttribute("params");
		if(macro)
			macro = config.macros[macro];
		if(macro && macro.refresh)
			macro.refresh(e,params);
		return true;
		}
};

config.refresherData = {
	styleSheet: "StyleSheet",
	defaultStyleSheet: "StyleSheet",
	pageTemplate: "PageTemplate",
	defaultPageTemplate: "PageTemplate",
	colorPalette: "ColorPalette",
	defaultColorPalette: "ColorPalette"
};

function refreshElements(root,changeList)
{
	var nodes = root.childNodes;
	for(var c=0; c<nodes.length; c++) {
		var e = nodes[c], type = null;
		if(e.getAttribute && (e.tagName ? e.tagName != "IFRAME" : true))
			type = e.getAttribute("refresh");
		var refresher = config.refreshers[type];
		var refreshed = false;
		if(refresher != undefined)
			refreshed = refresher(e,changeList);
		if(e.hasChildNodes() && !refreshed)
			refreshElements(e,changeList);
	}
}

function applyHtmlMacros(root,tiddler)
{
	var e = root.firstChild;
	while(e) {
		var nextChild = e.nextSibling;
		if(e.getAttribute) {
			var macro = e.getAttribute("macro");
			if(macro) {
				e.removeAttribute("macro");
				var params = "";
				var p = macro.indexOf(" ");
				if(p != -1) {
					params = macro.substr(p+1);
					macro = macro.substr(0,p);
				}
				invokeMacro(e,macro,params,null,tiddler);
			}
		}
		if(e.hasChildNodes())
			applyHtmlMacros(e,tiddler);
		e = nextChild;
	}
}

function refreshPageTemplate(title)
{
	var stash = jQuery("<div/>").appendTo("body").hide()[0];
	var display = story.getContainer();
	var nodes,t;
	if(display) {
		nodes = display.childNodes;
		for(t=nodes.length-1; t>=0; t--)
			stash.appendChild(nodes[t]);
	}
	var wrapper = document.getElementById("contentWrapper");

	var isAvailable = function(title) {
		var s = title ? title.indexOf(config.textPrimitives.sectionSeparator) : -1;
		if(s!=-1)
			title = title.substr(0,s);
		return store.tiddlerExists(title) || store.isShadowTiddler(title);
	};
	if(!title || !isAvailable(title))
		title = config.refresherData.pageTemplate;
	if(!isAvailable(title))
		title = config.refresherData.defaultPageTemplate; //# this one is always avaialable
	wrapper.innerHTML = store.getRecursiveTiddlerText(title,null,10);
	applyHtmlMacros(wrapper);
	refreshElements(wrapper);
	display = story.getContainer();
	removeChildren(display);
	if(!display)
		display = createTiddlyElement(wrapper,"div",story.containerId());
	nodes = stash.childNodes;
	for(t=nodes.length-1; t>=0; t--)
		display.appendChild(nodes[t]);
	removeNode(stash);
}

function refreshDisplay(hint)
{
	if(typeof hint == "string")
		hint = [hint];
	var e = document.getElementById("contentWrapper");
	refreshElements(e,hint);
	if(backstage.isPanelVisible()) {
		e = document.getElementById("backstage");
		refreshElements(e,hint);
	}
}

function refreshPageTitle()
{
	document.title = getPageTitle();
}

function getPageTitle()
{
	return wikifyPlain("WindowTitle");
}

function refreshStyles(title,doc)
{
	setStylesheet(title == null ? "" : store.getRecursiveTiddlerText(title,"",10),title,doc || document);
}

function refreshColorPalette(title)
{
	if(!startingUp)
		refreshAll();
}

function refreshAll()
{
	refreshPageTemplate();
	refreshDisplay();
	refreshStyles("StyleSheetLayout");
	refreshStyles("StyleSheetColors");
	refreshStyles(config.refresherData.styleSheet);
	refreshStyles("StyleSheetPrint");
}

//--
//-- Option handling
//--

config.optionHandlers = {
	'txt': {
		get: function(name) {return encodeCookie(config.options[name].toString());},
		set: function(name,value) {config.options[name] = decodeCookie(value);}
	},
	'chk': {
		get: function(name) {return config.options[name] ? 'true' : 'false';},
		set: function(name,value) {config.options[name] = value == 'true';}
	}
};

function setOption(name,value)
{
	var optType = name.substr(0,3);
	if(config.optionHandlers[optType] && config.optionHandlers[optType].set)
		config.optionHandlers[optType].set(name,value);
}

// Gets the value of an option as a string. Most code should just read from config.options.* directly
function getOption(name)
{
	var optType = name.substr(0,3);
	return config.optionHandlers[optType] && config.optionHandlers[optType].get ? config.optionHandlers[optType].get(name) : null;
}

function loadOptions()
{
	if(safeMode)
		return;
	loadCookies();
	loadSystemSettings();
}
// @Deprecated; retained for backwards compatibility
var loadOptionsCookie = loadOptions;

function getCookies()
{
	var cookieList = document.cookie.split(';');
	var cookies = {};
	for(var i=0; i<cookieList.length; i++) {
		var p = cookieList[i].indexOf('=');
		if(p != -1) {
			var name = cookieList[i].substr(0,p).trim();
			var value = cookieList[i].substr(p+1).trim();
			cookies[name] = value;
		}
	}
	return cookies;
}

function loadCookies()
{
	var cookies = getCookies();
	if(cookies['TiddlyWiki']) {
		cookies = cookies['TiddlyWiki'].decodeHashMap();
	}
	for(var i in cookies) {
		if(config.optionsSource[i] != 'setting') {
			setOption(i,cookies[i]);
		}
	}
}

function loadSystemSettings()
{
	var settings = store.calcAllSlices('SystemSettings');
	config.optionsSource = {};
	for(var key in settings) {
		setOption(key,settings[key]);
		config.optionsSource[key] = 'setting';
	}
}

function onSystemSettingsChange()
{
	if(!startingUp) {
		loadSystemSettings();
	}
}

function saveOption(name)
{
	if(safeMode)
		return;
	if(name.match(/[()\s]/g, '_')) {
		alert(config.messages.invalidCookie.format([name]));
		return;
	}
	saveCookie(name);
	if(config.optionsSource[name] == 'setting') {
		saveSystemSetting(name,true);
	}
}
// @Deprecated; retained for backwards compatibility
var saveOptionCookie = saveOption;

function removeCookie(name)
{
	document.cookie = name + '=; expires=Thu, 01-Jan-1970 00:00:01 UTC; path=/;';
}

function saveCookie(name)
{
	var cookies = {};
	for(var key in config.options) {
		var value = getOption(key);
		value = value == null ? 'false' : value;
		cookies[key] = value;
	}
	document.cookie = 'TiddlyWiki=' + String.encodeHashMap(cookies) + '; expires=Fri, 1 Jan 2038 12:00:00 UTC; path=/';
	cookies = getCookies();
	for(var c in cookies) {
		var optType = c.substr(0,3);
		if(config.optionHandlers[optType])
			removeCookie(c);
	}
}

function saveSystemSetting(name,saveFile)
{
	var title = 'SystemSettings';
	var slice = store.getTiddlerSlice(title,name);
	if(readOnly || slice === getOption(name)) {
		return; //# don't save if read-only or the option hasn't changed
	}
	var slices = store.calcAllSlices(title);
	var key;
	for(key in config.optionsSource) {
		var value = getOption(key) || '';
		if(slices[key] !== value) {
			slices[key] = value;
		}
	}
	var text = [];
	for(key in slices) {
		text.push('%0: %1'.format([key,slices[key]]));
	}
	text = text.sort().join('\n');
	var storeWasDirty = store.isDirty();
	var tiddler = store.getTiddler(title);
	if(tiddler) {
		tiddler.text = text;
		tiddler = store.saveTiddler(tiddler);
	} else {
		tiddler = store.saveTiddler(title,title,text,'System',new Date(),['excludeLists'],config.defaultCustomFields);
	}
	if(saveFile) {
		if(storeWasDirty == false && story.areAnyDirty() == false) {
			saveChanges(null,[tiddler]);
		} else {
			autoSaveChanges(null,[tiddler]);
		
		}
    }
}

function encodeCookie(s)
{
	return escape(convertUnicodeToHtmlEntities(s));
}

function decodeCookie(s)
{
	s = unescape(s);
	var re = /&#[0-9]{1,5};/g;
	return s.replace(re,function($0) {return String.fromCharCode(eval($0.replace(/[&#;]/g,'')));});
}

config.macros.option.genericCreate = function(place,type,opt,className,desc)
{
	var typeInfo = config.macros.option.types[type];
	var c = document.createElement(typeInfo.elementType);
	if(typeInfo.typeValue)
		c.setAttribute('type',typeInfo.typeValue);
	c[typeInfo.eventName] = typeInfo.onChange;
	c.setAttribute('option',opt);
	c.className = className || typeInfo.className;
	if(config.optionsDesc[opt])
		c.setAttribute('title',config.optionsDesc[opt]);
	place.appendChild(c);
	if(desc != 'no')
		createTiddlyText(place,config.optionsDesc[opt] || opt);
	c[typeInfo.valueField] = config.options[opt];
	return c;
};

config.macros.option.genericOnChange = function(e)
{
	var opt = this.getAttribute('option');
	if(opt) {
		var optType = opt.substr(0,3);
		var handler = config.macros.option.types[optType];
		if(handler.elementType && handler.valueField)
			config.macros.option.propagateOption(opt,handler.valueField,this[handler.valueField],handler.elementType,this);
	}
	return true;
};

config.macros.option.types = {
	'txt': {
		elementType: 'input',
		valueField: 'value',
		eventName: 'onchange',
		className: 'txtOptionInput',
		create: config.macros.option.genericCreate,
		onChange: config.macros.option.genericOnChange
	},
	'chk': {
		elementType: 'input',
		valueField: 'checked',
		eventName: 'onclick',
		className: 'chkOptionInput',
		typeValue: 'checkbox',
		create: config.macros.option.genericCreate,
		onChange: config.macros.option.genericOnChange
	}
};

config.macros.option.propagateOption = function(opt,valueField,value,elementType,elem)
{
	config.options[opt] = value;
	saveOption(opt);
	var nodes = document.getElementsByTagName(elementType);
	for(var t=0; t<nodes.length; t++) {
		var optNode = nodes[t].getAttribute('option');
		if(opt == optNode && nodes[t]!=elem)
			nodes[t][valueField] = value;
	}
};

config.macros.option.handler = function(place,macroName,params,wikifier,paramString)
{
	params = paramString.parseParams('anon',null,true,false,false);
	var opt = (params[1] && params[1].name == 'anon') ? params[1].value : getParam(params,'name',null);
	var className = (params[2] && params[2].name == 'anon') ? params[2].value : getParam(params,'class',null);
	var desc = getParam(params,'desc','no');
	var type = opt.substr(0,3);
	var h = config.macros.option.types[type];
	if(h && h.create)
		h.create(place,type,opt,className,desc);
};

config.macros.options.handler = function(place,macroName,params,wikifier,paramString)
{
	params = paramString.parseParams('anon',null,true,false,false);
	var showUnknown = getParam(params,'showUnknown','no');
	var wizard = new Wizard();
	wizard.createWizard(place,this.wizardTitle);
	wizard.addStep(this.step1Title,this.step1Html);
	var markList = wizard.getElement('markList');
	var chkUnknown = wizard.getElement('chkUnknown');
	chkUnknown.checked = showUnknown == 'yes';
	chkUnknown.onchange = this.onChangeUnknown;
	var listWrapper = document.createElement('div');
	markList.parentNode.insertBefore(listWrapper,markList);
	wizard.setValue('listWrapper',listWrapper);
	this.refreshOptions(listWrapper,showUnknown == 'yes');
};

config.macros.options.refreshOptions = function(listWrapper,showUnknown)
{
	var opts = [];
	for(var n in config.options) {
		var opt = {};
		opt.option = '';
		opt.name = n;
		opt.lowlight = !config.optionsDesc[n];
		opt.description = opt.lowlight ? this.unknownDescription : config.optionsDesc[n];
		if(!opt.lowlight || showUnknown)
			opts.push(opt);
	}
	opts.sort(function(a,b) {return a.name.substr(3) < b.name.substr(3) ? -1 : (a.name.substr(3) == b.name.substr(3) ? 0 : +1);});
	var listview = ListView.create(listWrapper,opts,this.listViewTemplate);
	for(n=0; n<opts.length; n++) {
		var type = opts[n].name.substr(0,3);
		var h = config.macros.option.types[type];
		if(h && h.create) {
			h.create(opts[n].colElements['option'],type,opts[n].name,null,'no');
		}
	}
};

config.macros.options.onChangeUnknown = function(e)
{
	var wizard = new Wizard(this);
	var listWrapper = wizard.getValue('listWrapper');
	removeChildren(listWrapper);
	config.macros.options.refreshOptions(listWrapper,this.checked);
	return false;
};

//--
//-- Saving
//--

var saveUsingSafari = false;

var startSaveArea = '<div id="' + 'storeArea">'; // Split up into two so that indexOf() of this source doesn't find it
var endSaveArea = '</d' + 'iv>';

// If there are unsaved changes, force the user to confirm before exitting
function confirmExit()
{
	hadConfirmExit = true;
	if((store && store.isDirty && store.isDirty()) || (story && story.areAnyDirty && story.areAnyDirty()))
		return config.messages.confirmExit;
}

// Give the user a chance to save changes before exitting
function checkUnsavedChanges()
{
	if(store && store.isDirty && store.isDirty() && window.hadConfirmExit === false) {
		if(confirm(config.messages.unsavedChangesWarning))
			saveChanges();
	}
}

function updateLanguageAttribute(s)
{
	if(config.locale) {
		var mRE = /(<html(?:.*?)?)(?: xml:lang\="([a-z]+)")?(?: lang\="([a-z]+)")?>/;
		var m = mRE.exec(s);
		if(m) {
			var t = m[1];
			if(m[2])
				t += ' xml:lang="' + config.locale + '"';
			if(m[3])
				t += ' lang="' + config.locale + '"';
			t += ">";
			s = s.substr(0,m.index) + t + s.substr(m.index+m[0].length);
		}
	}
	return s;
}

function updateMarkupBlock(s,blockName,tiddlerName)
{
	return s.replaceChunk(
			"<!--%0-START-->".format([blockName]),
			"<!--%0-END-->".format([blockName]),
			"\n" + convertUnicodeToFileFormat(store.getRecursiveTiddlerText(tiddlerName,"")) + "\n");
}

function updateOriginal(original,posDiv,localPath)
{
	if(!posDiv)
		posDiv = locateStoreArea(original);
	if(!posDiv) {
		alert(config.messages.invalidFileError.format([localPath]));
		return null;
	}
	var revised = original.substr(0,posDiv[0] + startSaveArea.length) + "\n" +
				convertUnicodeToFileFormat(store.allTiddlersAsHtml()) + "\n" +
				original.substr(posDiv[1]);
	var newSiteTitle = convertUnicodeToFileFormat(getPageTitle()).htmlEncode();
	revised = revised.replaceChunk("<title"+">","</title"+">"," " + newSiteTitle + " ");
	revised = updateLanguageAttribute(revised);
	revised = updateMarkupBlock(revised,"PRE-HEAD","MarkupPreHead");
	revised = updateMarkupBlock(revised,"POST-HEAD","MarkupPostHead");
	revised = updateMarkupBlock(revised,"PRE-BODY","MarkupPreBody");
	revised = updateMarkupBlock(revised,"POST-SCRIPT","MarkupPostBody");
	return revised;
}

function locateStoreArea(original)
{
	// Locate the storeArea div's
	var posOpeningDiv = original.indexOf(startSaveArea);
	var limitClosingDiv = original.indexOf("<"+"!--POST-STOREAREA--"+">");
	if(limitClosingDiv == -1)
		limitClosingDiv = original.indexOf("<"+"!--POST-BODY-START--"+">");
	var posClosingDiv = original.lastIndexOf(endSaveArea,limitClosingDiv == -1 ? original.length : limitClosingDiv);
	return (posOpeningDiv != -1 && posClosingDiv != -1) ? [posOpeningDiv,posClosingDiv] : null;
}

function autoSaveChanges(onlyIfDirty,tiddlers)
{
	if(config.options.chkAutoSave)
		saveChanges(onlyIfDirty,tiddlers);
}

function loadOriginal(localPath)
{
	return loadFile(localPath);
}

// Save this tiddlywiki with the pending changes
function saveChanges(onlyIfDirty,tiddlers)
{
	if(onlyIfDirty && !store.isDirty())
		return;
	clearMessage();
	var t0 = new Date();
	var msg = config.messages;
	var originalPath = document.location.toString();
	if(originalPath.substr(0,5) != "file:") {
		alert(msg.notFileUrlError);
		if(store.tiddlerExists(msg.saveInstructions))
			story.displayTiddler(null,msg.saveInstructions);
		return;
	}
	var localPath = getLocalPath(originalPath);
	var original = loadOriginal(localPath);
	if(original == null) {
		alert(msg.cantSaveError);
		if(store.tiddlerExists(msg.saveInstructions))
			story.displayTiddler(null,msg.saveInstructions);
		return;
	}
	var posDiv = locateStoreArea(original);
	if(!posDiv) {
		alert(msg.invalidFileError.format([localPath]));
		return;
	}
	saveMain(localPath,original,posDiv);
	if(config.options.chkSaveBackups)
		saveBackup(localPath,original);
	if(config.options.chkSaveEmptyTemplate)
		saveEmpty(localPath,original,posDiv);
	if(config.options.chkGenerateAnRssFeed && saveRss instanceof Function)
		saveRss(localPath);
	if(config.options.chkDisplayInstrumentation)
		displayMessage("saveChanges " + (new Date()-t0) + " ms");
}

function saveMain(localPath,original,posDiv)
{
	var save;
	try {
		var revised = updateOriginal(original,posDiv,localPath);
		save = saveFile(localPath,revised);
	} catch (ex) {
		showException(ex);
	}
	if(save) {
		displayMessage(config.messages.mainSaved,"file://" + localPath);
		store.setDirty(false);
	} else {
		alert(config.messages.mainFailed);
	}
}

function saveBackup(localPath,original)
{
	var backupPath = getBackupPath(localPath);
	var backup = copyFile(backupPath,localPath);
	if(!backup)
		backup = saveFile(backupPath,original);
	if(backup)
		displayMessage(config.messages.backupSaved,"file://" + backupPath);
	else
		alert(config.messages.backupFailed);
}

function saveEmpty(localPath,original,posDiv)
{
	var emptyPath,p;
	if((p = localPath.lastIndexOf("/")) != -1)
		emptyPath = localPath.substr(0,p) + "/";
	else if((p = localPath.lastIndexOf("\\")) != -1)
		emptyPath = localPath.substr(0,p) + "\\";
	else
		emptyPath = localPath + ".";
	emptyPath += "empty.html";
	var empty = original.substr(0,posDiv[0] + startSaveArea.length) + original.substr(posDiv[1]);
	var emptySave = saveFile(emptyPath,empty);
	if(emptySave)
		displayMessage(config.messages.emptySaved,"file://" + emptyPath);
	else
		alert(config.messages.emptyFailed);
}

function getLocalPath(origPath)
{
	var originalPath = convertUriToUTF8(origPath,config.options.txtFileSystemCharSet);
	// Remove any location or query part of the URL
	var argPos = originalPath.indexOf("?");
	if(argPos != -1)
		originalPath = originalPath.substr(0,argPos);
	var hashPos = originalPath.indexOf("#");
	if(hashPos != -1)
		originalPath = originalPath.substr(0,hashPos);
	// Convert file://localhost/ to file:///
	if(originalPath.indexOf("file://localhost/") == 0)
		originalPath = "file://" + originalPath.substr(16);
	// Convert to a native file format
	var localPath;
	if(originalPath.charAt(9) == ":") // pc local file
		localPath = unescape(originalPath.substr(8)).replace(new RegExp("/","g"),"\\");
	else if(originalPath.indexOf("file://///") == 0) // FireFox pc network file
		localPath = "\\\\" + unescape(originalPath.substr(10)).replace(new RegExp("/","g"),"\\");
	else if(originalPath.indexOf("file:///") == 0) // mac/unix local file
		localPath = unescape(originalPath.substr(7));
	else if(originalPath.indexOf("file:/") == 0) // mac/unix local file
		localPath = unescape(originalPath.substr(5));
	else // pc network file
		localPath = "\\\\" + unescape(originalPath.substr(7)).replace(new RegExp("/","g"),"\\");
	return localPath;
}

function getBackupPath(localPath,title,extension)
{
	var slash = "\\";
	var dirPathPos = localPath.lastIndexOf("\\");
	if(dirPathPos == -1) {
		dirPathPos = localPath.lastIndexOf("/");
		slash = "/";
	}
	var backupFolder = config.options.txtBackupFolder;
	if(!backupFolder || backupFolder == "")
		backupFolder = ".";
	var backupPath = localPath.substr(0,dirPathPos) + slash + backupFolder + localPath.substr(dirPathPos);
	backupPath = backupPath.substr(0,backupPath.lastIndexOf(".")) + ".";
	if(title)
		backupPath += title.replace(/[\\\/\*\?\":<> ]/g,"_") + ".";
	backupPath += (new Date()).convertToYYYYMMDDHHMMSSMMM() + "." + (extension || "html");
	return backupPath;
}

//--
//-- RSS Saving
//--

function saveRss(localPath)
{
	var rssPath = localPath.substr(0,localPath.lastIndexOf(".")) + ".xml";
	if(saveFile(rssPath,convertUnicodeToFileFormat(generateRss())))
		displayMessage(config.messages.rssSaved,"file://" + rssPath);
	else
		alert(config.messages.rssFailed);
}

tiddlerToRssItem = function(tiddler,uri)
{
	var s = "<title" + ">" + tiddler.title.htmlEncode() + "</title" + ">\n";
	s += "<description>" + wikifyStatic(tiddler.text,null,tiddler).htmlEncode() + "</description>\n";
	for(var i=0; i<tiddler.tags.length; i++)
		s += "<category>" + tiddler.tags[i] + "</category>\n";
	s += "<link>" + uri + "#" + encodeURIComponent(String.encodeTiddlyLink(tiddler.title)) + "</link>\n";
	s +="<pubDate>" + tiddler.modified.toGMTString() + "</pubDate>\n";
	return s;
};

function generateRss()
{
	var s = [];
	var d = new Date();
	var u = store.getTiddlerText("SiteUrl");
	// Assemble the header
	s.push("<" + "?xml version=\"1.0\"?" + ">");
	s.push("<rss version=\"2.0\">");
	s.push("<channel>");
	s.push("<title" + ">" + wikifyPlain("SiteTitle").htmlEncode() + "</title" + ">");
	if(u)
		s.push("<link>" + u.htmlEncode() + "</link>");
	s.push("<description>" + wikifyPlain("SiteSubtitle").htmlEncode() + "</description>");
	s.push("<language>" + config.locale + "</language>");
	s.push("<copyright>Copyright " + d.getFullYear() + " " + config.options.txtUserName.htmlEncode() + "</copyright>");
	s.push("<pubDate>" + d.toGMTString() + "</pubDate>");
	s.push("<lastBuildDate>" + d.toGMTString() + "</lastBuildDate>");
	s.push("<docs>http://blogs.law.harvard.edu/tech/rss</docs>");
	s.push("<generator>TiddlyWiki " + formatVersion() + "</generator>");
	// The body
	var tiddlers = store.getTiddlers("modified","excludeLists");
	var n = config.numRssItems > tiddlers.length ? 0 : tiddlers.length-config.numRssItems;
	for(var i=tiddlers.length-1; i>=n; i--) {
		s.push("<item>\n" + tiddlerToRssItem(tiddlers[i],u) + "\n</item>");
	}
	// And footer
	s.push("</channel>");
	s.push("</rss>");
	// Save it all
	return s.join("\n");
}

//--
//-- Filesystem code
//--

function convertUTF8ToUnicode(u)
{
	return config.browser.isOpera || !window.netscape ? manualConvertUTF8ToUnicode(u) : mozConvertUTF8ToUnicode(u);
}

function manualConvertUTF8ToUnicode(utf)
{
	var uni = utf;
	var src = 0;
	var dst = 0;
	var b1, b2, b3;
	var c;
	while(src < utf.length) {
		b1 = utf.charCodeAt(src++);
		if(b1 < 0x80) {
			dst++;
		} else if(b1 < 0xE0) {
			b2 = utf.charCodeAt(src++);
			c = String.fromCharCode(((b1 & 0x1F) << 6) | (b2 & 0x3F));
			uni = uni.substring(0,dst++).concat(c,utf.substr(src));
		} else {
			b2 = utf.charCodeAt(src++);
			b3 = utf.charCodeAt(src++);
			c = String.fromCharCode(((b1 & 0xF) << 12) | ((b2 & 0x3F) << 6) | (b3 & 0x3F));
			uni = uni.substring(0,dst++).concat(c,utf.substr(src));
		}
	}
	return uni;
}

function mozConvertUTF8ToUnicode(u)
{
	try {
		netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
		var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
		converter.charset = "UTF-8";
	} catch(ex) {
		return manualConvertUTF8ToUnicode(u);
	} // fallback
	var s = converter.ConvertToUnicode(u);
	var fin = converter.Finish();
	return fin.length > 0 ? s+fin : s;
}

function convertUnicodeToFileFormat(s)
{
	return config.browser.isOpera || !window.netscape ? (config.browser.isIE ? convertUnicodeToHtmlEntities(s) : s) : mozConvertUnicodeToUTF8(s);
}

function convertUnicodeToHtmlEntities(s)
{
	var re = /[^\u0000-\u007F]/g;
	return s.replace(re,function($0) {return "&#" + $0.charCodeAt(0).toString() + ";";});
}

function convertUnicodeToUTF8(s)
{
// return convertUnicodeToFileFormat to allow plugin migration
	return convertUnicodeToFileFormat(s);
}

function manualConvertUnicodeToUTF8(s)
{
	return unescape(encodeURIComponent(s));
}

function mozConvertUnicodeToUTF8(s)
{
	try {
		netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
		var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
		converter.charset = "UTF-8";
	} catch(ex) {
		return manualConvertUnicodeToUTF8(s);
	} // fallback
	var u = converter.ConvertFromUnicode(s);
	var fin = converter.Finish();
	return fin.length > 0 ? u + fin : u;
}

function convertUriToUTF8(uri,charSet)
{
	if(window.netscape == undefined || charSet == undefined || charSet == "")
		return uri;
	try {
		netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
		var converter = Components.classes["@mozilla.org/intl/utf8converterservice;1"].getService(Components.interfaces.nsIUTF8ConverterService);
	} catch(ex) {
		return uri;
	}
	return converter.convertURISpecToUTF8(uri,charSet);
}

function copyFile(dest,source)
{
	return config.browser.isIE ? ieCopyFile(dest,source) : false;
}

function saveFile(fileUrl,content)
{
	var r = mozillaSaveFile(fileUrl,content);
	if(!r)
		r = ieSaveFile(fileUrl,content);
	if(!r)
		r = javaSaveFile(fileUrl,content);
	return r;
}

function loadFile(fileUrl)
{
	var r = mozillaLoadFile(fileUrl);
	if((r == null) || (r == false))
		r = ieLoadFile(fileUrl);
	if((r == null) || (r == false))
		r = javaLoadFile(fileUrl);
	return r;
}

function ieCreatePath(path)
{
	try {
		var fso = new ActiveXObject("Scripting.FileSystemObject");
	} catch(ex) {
		return null;
	}

	var pos = path.lastIndexOf("\\");
	if(pos==-1)
		pos = path.lastIndexOf("/");
	if(pos!=-1)
		path = path.substring(0,pos+1);

	var scan = [path];
	var parent = fso.GetParentFolderName(path);
	while(parent && !fso.FolderExists(parent)) {
		scan.push(parent);
		parent = fso.GetParentFolderName(parent);
	}

	for(i=scan.length-1;i>=0;i--) {
		if(!fso.FolderExists(scan[i])) {
			fso.CreateFolder(scan[i]);
		}
	}
	return true;
}

// Returns null if it can't do it, false if there's an error, true if it saved OK
function ieSaveFile(filePath,content)
{
	ieCreatePath(filePath);
	try {
		var fso = new ActiveXObject("Scripting.FileSystemObject");
	} catch(ex) {
		return null;
	}
	var file = fso.OpenTextFile(filePath,2,-1,0);
	file.Write(content);
	file.Close();
	return true;
}

// Returns null if it can't do it, false if there's an error, or a string of the content if successful
function ieLoadFile(filePath)
{
	try {
		var fso = new ActiveXObject("Scripting.FileSystemObject");
		var file = fso.OpenTextFile(filePath,1);
		var content = file.ReadAll();
		file.Close();
	} catch(ex) {
		return null;
	}
	return content;
}

function ieCopyFile(dest,source)
{
	ieCreatePath(dest);
	try {
		var fso = new ActiveXObject("Scripting.FileSystemObject");
		fso.GetFile(source).Copy(dest);
	} catch(ex) {
		return false;
	}
	return true;
}

// Returns null if it can't do it, false if there's an error, true if it saved OK
function mozillaSaveFile(filePath,content)
{
	if(window.Components) {
		try {
			netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
			var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
			file.initWithPath(filePath);
			if(!file.exists())
				file.create(0,0664);
			var out = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
			out.init(file,0x20|0x02,00004,null);
			out.write(content,content.length);
			out.flush();
			out.close();
			return true;
		} catch(ex) {
			return false;
		}
	}
	return null;
}

// Returns null if it can't do it, false if there's an error, or a string of the content if successful
function mozillaLoadFile(filePath)
{
	if(window.Components) {
		try {
			netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
			var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
			file.initWithPath(filePath);
			if(!file.exists())
				return null;
			var inputStream = Components.classes["@mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream);
			inputStream.init(file,0x01,00004,null);
			var sInputStream = Components.classes["@mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream);
			sInputStream.init(inputStream);
			var contents = sInputStream.read(sInputStream.available());
			sInputStream.close();
			inputStream.close();
			return contents;
		} catch(ex) {
			return false;
		}
	}
	return null;
}

function javaUrlToFilename(url)
{
	var f = "//localhost";
	if(url.indexOf(f) == 0)
		return url.substring(f.length);
	var i = url.indexOf(":");
	return i > 0 ? url.substring(i-1) : url;
}

function javaSaveFile(filePath,content)
{
	try {
		if(document.applets["TiddlySaver"])
			return document.applets["TiddlySaver"].saveFile(javaUrlToFilename(filePath),"UTF-8",content);
	} catch(ex) {
	}
	try {
		var s = new java.io.PrintStream(new java.io.FileOutputStream(javaUrlToFilename(filePath)));
		s.print(content);
		s.close();
	} catch(ex) {
		return null;
	}
	return true;
}

function javaLoadFile(filePath)
{
	try {
		if(document.applets["TiddlySaver"])
			return String(document.applets["TiddlySaver"].loadFile(javaUrlToFilename(filePath),"UTF-8"));
	} catch(ex) {
	}
	var content = [];
	try {
		var r = new java.io.BufferedReader(new java.io.FileReader(javaUrlToFilename(filePath)));
		var line;
		while((line = r.readLine()) != null)
			content.push(new String(line));
		r.close();
	} catch(ex) {
		return null;
	}
	return content.join("\n");
}

//--
//-- Server adaptor base class
//--

function AdaptorBase()
{
	this.host = null;
	this.store = null;
	return this;
}

AdaptorBase.prototype.close = function()
{
	return true;
};

AdaptorBase.prototype.fullHostName = function(host)
{
	if(!host)
		return '';
	host = host.trim();
	if(!host.match(/:\/\//))
		host = 'http://' + host;
	if(host.substr(host.length-1) == '/')
		host = host.substr(0,host.length-1);
	return host;
};

AdaptorBase.minHostName = function(host)
{
	return host;
};

AdaptorBase.prototype.setContext = function(context,userParams,callback)
{
	if(!context) context = {};
	context.userParams = userParams;
	if(callback) context.callback = callback;
	context.adaptor = this;
	if(!context.host)
		context.host = this.host;
	context.host = this.fullHostName(context.host);
	if(!context.workspace)
		context.workspace = this.workspace;
	return context;
};

// Open the specified host
AdaptorBase.prototype.openHost = function(host,context,userParams,callback)
{
	this.host = host;
	context = this.setContext(context,userParams,callback);
	context.status = true;
	if(callback)
		window.setTimeout(function() {context.callback(context,userParams);},10);
	return true;
};

// Open the specified workspace
AdaptorBase.prototype.openWorkspace = function(workspace,context,userParams,callback)
{
	this.workspace = workspace;
	context = this.setContext(context,userParams,callback);
	context.status = true;
	if(callback)
		window.setTimeout(function() {callback(context,userParams);},10);
	return true;
};

//--
//-- Server adaptor for talking to static TiddlyWiki files
//--

function FileAdaptor()
{
}

FileAdaptor.prototype = new AdaptorBase();

FileAdaptor.serverType = 'file';
FileAdaptor.serverLabel = 'TiddlyWiki';

FileAdaptor.loadTiddlyWikiCallback = function(status,context,responseText,url,xhr)
{
	context.status = status;
	if(!status) {
		context.statusText = "Error reading file";
	} else {
		context.adaptor.store = new TiddlyWiki();
		if(!context.adaptor.store.importTiddlyWiki(responseText)) {
			context.statusText = config.messages.invalidFileError.format([url]);
			context.status = false;
		}
	}
	context.complete(context,context.userParams);
};

// Get the list of workspaces on a given server
FileAdaptor.prototype.getWorkspaceList = function(context,userParams,callback)
{
	context = this.setContext(context,userParams,callback);
	context.workspaces = [{title:"(default)"}];
	context.status = true;
	if(callback)
		window.setTimeout(function() {callback(context,userParams);},10);
	return true;
};

// Gets the list of tiddlers within a given workspace
FileAdaptor.prototype.getTiddlerList = function(context,userParams,callback,filter)
{
	context = this.setContext(context,userParams,callback);
	if(!context.filter)
		context.filter = filter;
	context.complete = FileAdaptor.getTiddlerListComplete;
	if(this.store) {
		var ret = context.complete(context,context.userParams);
	} else {
		ret = loadRemoteFile(context.host,FileAdaptor.loadTiddlyWikiCallback,context);
		if(typeof ret != "string")
			ret = true;
	}
	return ret;
};

FileAdaptor.getTiddlerListComplete = function(context,userParams)
{
	if(context.status) {
		if(context.filter) {
			context.tiddlers = context.adaptor.store.filterTiddlers(context.filter);
		} else {
			context.tiddlers = [];
			context.adaptor.store.forEachTiddler(function(title,tiddler) {context.tiddlers.push(tiddler);});
		}
		for(var i=0; i<context.tiddlers.length; i++) {
			context.tiddlers[i].fields['server.type'] = FileAdaptor.serverType;
			context.tiddlers[i].fields['server.host'] = AdaptorBase.minHostName(context.host);
			context.tiddlers[i].fields['server.page.revision'] = context.tiddlers[i].modified.convertToYYYYMMDDHHMM();
		}
		context.status = true;
	}
	if(context.callback) {
		window.setTimeout(function() {context.callback(context,userParams);},10);
	}
	return true;
};

FileAdaptor.prototype.generateTiddlerInfo = function(tiddler)
{
	var info = {};
	info.uri = tiddler.fields['server.host'] + "#" + tiddler.title;
	return info;
};

// Retrieve a tiddler from a given workspace on a given server
FileAdaptor.prototype.getTiddler = function(title,context,userParams,callback)
{
	context = this.setContext(context,userParams,callback);
	context.title = title;
	context.complete = FileAdaptor.getTiddlerComplete;
	return context.adaptor.store ?
		context.complete(context,context.userParams) :
		loadRemoteFile(context.host,FileAdaptor.loadTiddlyWikiCallback,context);
};

FileAdaptor.getTiddlerComplete = function(context,userParams)
{
	var t = context.adaptor.store.fetchTiddler(context.title);
	if(t) {
		t.fields['server.type'] = FileAdaptor.serverType;
		t.fields['server.host'] = AdaptorBase.minHostName(context.host);
		t.fields['server.page.revision'] = t.modified.convertToYYYYMMDDHHMM();
		context.tiddler = t;
		context.status = true;
	} else { //# tiddler does not exist in document
		context.status = false;
	}
	if(context.allowSynchronous) {
		context.isSynchronous = true;
		context.callback(context,userParams);
	} else {
		window.setTimeout(function() {context.callback(context,userParams);},10);
	}
	return true;
};

FileAdaptor.prototype.close = function()
{
	this.store = null;
};

config.adaptors[FileAdaptor.serverType] = FileAdaptor;

config.defaultAdaptor = FileAdaptor.serverType;

//--
//-- HTTP request code
//--

function ajaxReq(args)
{
	if(window.Components && window.netscape && window.netscape.security && document.location.protocol.indexOf("http") == -1)
		window.netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
	return jQuery.ajax(args);
}

//--
//-- TiddlyWiki-specific utility functions
//--

// Returns TiddlyWiki version string
function formatVersion(v)
{
	v = v || version;
	return v.major + "." + v.minor + "." + v.revision +
		(v.alpha ? " (alpha " + v.alpha + ")" : "") +
		(v.beta ? " (beta " + v.beta + ")" : "");
}

function compareVersions(v1,v2)
{
	var a = ["major","minor","revision"];
	for(var i = 0; i<a.length; i++) {
		var x1 = v1[a[i]] || 0;
		var x2 = v2[a[i]] || 0;
		if(x1<x2)
			return 1;
		if(x1>x2)
			return -1;
	}
	x1 = v1.beta || 9999;
	x2 = v2.beta || 9999;
	if(x1<x2)
		return 1;
	return x1 > x2 ? -1 : 0;
}

function createTiddlyButton(parent,text,tooltip,action,className,id,accessKey,attribs)
{
	var btn = document.createElement("a");
	if(action) {
		btn.onclick = action;
		btn.setAttribute("href","javascript:;");
	}
	if(tooltip)
		btn.setAttribute("title",tooltip);
	if(text)
		btn.appendChild(document.createTextNode(text));
	btn.className = className || "button";
	if(id)
		btn.id = id;
	if(attribs) {
		for(var i in attribs) {
			btn.setAttribute(i,attribs[i]);
		}
	}
	if(parent)
		parent.appendChild(btn);
	if(accessKey)
		btn.setAttribute("accessKey",accessKey);
	return btn;
}

function createTiddlyLink(place,title,includeText,className,isStatic,linkedFromTiddler,noToggle)
{
	var text = includeText ? title : null;
	var i = getTiddlyLinkInfo(title,className);
	var btn = isStatic ? createExternalLink(place,store.getTiddlerText("SiteUrl",null) + "#" + title) : createTiddlyButton(place,text,i.subTitle,onClickTiddlerLink,i.classes);
	if(isStatic)
		btn.className += ' ' + className;
	btn.setAttribute("refresh","link");
	btn.setAttribute("tiddlyLink",title);
	if(noToggle)
		btn.setAttribute("noToggle","true");
	if(linkedFromTiddler) {
		var fields = linkedFromTiddler.getInheritedFields();
		if(fields)
			btn.setAttribute("tiddlyFields",fields);
	}
	return btn;
}

function refreshTiddlyLink(e,title)
{
	var i = getTiddlyLinkInfo(title,e.className);
	e.className = i.classes;
	e.title = i.subTitle;
}

function getTiddlyLinkInfo(title,currClasses)
{
	var classes = currClasses ? currClasses.split(" ") : [];
	classes.pushUnique("tiddlyLink");
	var tiddler = store.fetchTiddler(title);
	var subTitle;
	if(tiddler) {
		subTitle = tiddler.getSubtitle();
		classes.pushUnique("tiddlyLinkExisting");
		classes.remove("tiddlyLinkNonExisting");
		classes.remove("shadow");
	} else {
		classes.remove("tiddlyLinkExisting");
		classes.pushUnique("tiddlyLinkNonExisting");
		if(store.isShadowTiddler(title)) {
			subTitle = config.messages.shadowedTiddlerToolTip.format([title]);
			classes.pushUnique("shadow");
		} else {
			subTitle = config.messages.undefinedTiddlerToolTip.format([title]);
			classes.remove("shadow");
		}
	}
	if(typeof config.annotations[title]=="string")
		subTitle = config.annotations[title];
	return {classes: classes.join(" "),subTitle: subTitle};
}

function createExternalLink(place,url,label)
{
	var link = document.createElement("a");
	link.className = "externalLink";
	link.href = url;
	link.title = config.messages.externalLinkTooltip.format([url]);
	if(config.options.chkOpenInNewWindow)
		link.target = "_blank";
	place.appendChild(link);
	if(label)
		createTiddlyText(link, label);
	return link;
}

// Event handler for clicking on a tiddly link
function onClickTiddlerLink(ev)
{
	var e = ev || window.event;
	var target = resolveTarget(e);
	var link = target;
	var title = null;
	var fields = null;
	var noToggle = null;
	do {
		title = link.getAttribute("tiddlyLink");
		fields = link.getAttribute("tiddlyFields");
		noToggle = link.getAttribute("noToggle");
		link = link.parentNode;
	} while(title == null && link != null);
	if(!store.isShadowTiddler(title)) {
		var f = fields ? fields.decodeHashMap() : {};
		fields = String.encodeHashMap(merge(f,config.defaultCustomFields,true));
	}
	if(title) {
		var toggling = e.metaKey || e.ctrlKey;
		if(config.options.chkToggleLinks)
			toggling = !toggling;
		if(noToggle)
			toggling = false;
		if(store.getTiddler(title))
			fields = null;
		story.displayTiddler(target,title,null,true,null,fields,toggling);
	}
	clearMessage();
	return false;
}

// Create a button for a tag with a popup listing all the tiddlers that it tags
function createTagButton(place,tag,excludeTiddler,title,tooltip)
{
	var btn = createTiddlyButton(place,title||tag,(tooltip||config.views.wikified.tag.tooltip).format([tag]),onClickTag);
	btn.setAttribute("tag",tag);
	if(excludeTiddler)
		btn.setAttribute("tiddler",excludeTiddler);
	return btn;
}

// Event handler for clicking on a tiddler tag
function onClickTag(ev)
{
	var e = ev || window.event;
	var popup = Popup.create(this);
	addClass(popup,"taggedTiddlerList");
	var tag = this.getAttribute("tag");
	var title = this.getAttribute("tiddler");
	if(popup && tag) {
		var tagged = tag.indexOf("[")==-1 ? store.getTaggedTiddlers(tag) : store.filterTiddlers(tag);
		var sortby = this.getAttribute("sortby");
		if(sortby&&sortby.length) {
			store.sortTiddlers(tagged,sortby);
		}
		var titles = [];
		var li,r;
		for(r=0;r<tagged.length;r++) {
			if(tagged[r].title != title)
				titles.push(tagged[r].title);
		}
		var lingo = config.views.wikified.tag;
		if(titles.length > 0) {
			var openAll = createTiddlyButton(createTiddlyElement(popup,"li"),lingo.openAllText.format([tag]),lingo.openAllTooltip,onClickTagOpenAll);
			openAll.setAttribute("tag",tag);
			openAll.setAttribute("sortby",sortby);
			createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
			for(r=0; r<titles.length; r++) {
				createTiddlyLink(createTiddlyElement(popup,"li"),titles[r],true);
			}
		} else {
			createTiddlyElement(popup,"li",null,"disabled",lingo.popupNone.format([tag]));
		}
		createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
		var h = createTiddlyLink(createTiddlyElement(popup,"li"),tag,false);
		createTiddlyText(h,lingo.openTag.format([tag]));
	}
	Popup.show();
	e.cancelBubble = true;
	if(e.stopPropagation) e.stopPropagation();
	return false;
}

// Event handler for 'open all' on a tiddler popup
function onClickTagOpenAll(ev)
{
	var tiddlers = store.getTaggedTiddlers(this.getAttribute("tag"));
	var sortby = this.getAttribute("sortby");
	if(sortby&&sortby.length) {
		store.sortTiddlers(tiddlers,sortby);
	}
	story.displayTiddlers(this,tiddlers);
	return false;
}

function onClickError(ev)
{
	var e = ev || window.event;
	var popup = Popup.create(this);
	var lines = this.getAttribute("errorText").split("\n");
	for(var t=0; t<lines.length; t++)
		createTiddlyElement(popup,"li",null,null,lines[t]);
	Popup.show();
	e.cancelBubble = true;
	if(e.stopPropagation) e.stopPropagation();
	return false;
}

function createTiddlyDropDown(place,onchange,options,defaultValue)
{
	var sel = createTiddlyElement(place,"select");
	sel.onchange = onchange;
	for(var t=0; t<options.length; t++) {
		var e = createTiddlyElement(sel,"option",null,null,options[t].caption);
		e.value = options[t].name;
		if(options[t].name == defaultValue)
			e.selected = true;
	}
	return sel;
}

function createTiddlyPopup(place,caption,tooltip,tiddler)
{
	if(tiddler.text) {
		createTiddlyLink(place,caption,true);
		var btn = createTiddlyButton(place,glyph("downArrow"),tooltip,onClickTiddlyPopup,"tiddlerPopupButton");
		btn.tiddler = tiddler;
	} else {
		createTiddlyText(place,caption);
	}
}

function onClickTiddlyPopup(ev)
{
	var e = ev || window.event;
	var tiddler = this.tiddler;
	if(tiddler.text) {
		var popup = Popup.create(this,"div","popupTiddler");
		wikify(tiddler.text,popup,null,tiddler);
		Popup.show();
	}
	if(e) e.cancelBubble = true;
	if(e && e.stopPropagation) e.stopPropagation();
	return false;
}

function createTiddlyError(place,title,text)
{
	var btn = createTiddlyButton(place,title,null,onClickError,"errorButton");
	if(text) btn.setAttribute("errorText",text);
}

function merge(dst,src,preserveExisting)
{
	for(var i in src) {
		if(!preserveExisting || dst[i] === undefined)
			dst[i] = src[i];
	}
	return dst;
}

// Returns a string containing the description of an exception, optionally prepended by a message
function exceptionText(e,message)
{
	var s = e.description || e.toString();
	return message ? "%0:\n%1".format([message,s]) : s;
}

// Displays an alert of an exception description with optional message
function showException(e,message)
{
	alert(exceptionText(e,message));
}

function alertAndThrow(m)
{
	alert(m);
	throw(m);
}

function glyph(name)
{
	var g = config.glyphs;
	var b = g.currBrowser;
	if(b == null) {
		b = 0;
		while(!g.browsers[b]() && b < g.browsers.length-1)
			b++;
		g.currBrowser = b;
	}
	if(!g.codes[name])
		return "";
	return g.codes[name][b];
}

if(!window.console) {
	console = {tiddlywiki:true,log:function(message) {displayMessage(message);}};
}

//-
//- Animation engine
//-

function Animator()
{
	this.running = 0; // Incremented at start of each animation, decremented afterwards. If zero, the interval timer is disabled
	this.timerID = 0; // ID of the timer used for animating
	this.animations = []; // List of animations in progress
	return this;
}

// Start animation engine
Animator.prototype.startAnimating = function() //# Variable number of arguments
{
	for(var t=0; t<arguments.length; t++)
		this.animations.push(arguments[t]);
	if(this.running == 0) {
		var me = this;
		this.timerID = window.setInterval(function() {me.doAnimate(me);},10);
	}
	this.running += arguments.length;
};

// Perform an animation engine tick, calling each of the known animation modules
Animator.prototype.doAnimate = function(me)
{
	var a = 0;
	while(a < me.animations.length) {
		var animation = me.animations[a];
		if(animation.tick()) {
			a++;
		} else {
			me.animations.splice(a,1);
			if(--me.running == 0)
				window.clearInterval(me.timerID);
		}
	}
};

Animator.slowInSlowOut = function(progress)
{
	return(1-((Math.cos(progress * Math.PI)+1)/2));
};

//--
//-- Morpher animation
//--

// Animate a set of properties of an element
function Morpher(element,duration,properties,callback)
{
	this.element = element;
	this.duration = duration;
	this.properties = properties;
	this.startTime = new Date();
	this.endTime = Number(this.startTime) + duration;
	this.callback = callback;
	this.tick();
	return this;
}

Morpher.prototype.assignStyle = function(element,style,value)
{
	switch(style) {
	case "-tw-vertScroll":
		window.scrollTo(findScrollX(),value);
		break;
	case "-tw-horizScroll":
		window.scrollTo(value,findScrollY());
		break;
	default:
		element.style[style] = value;
		break;
	}
};

Morpher.prototype.stop = function()
{
	for(var t=0; t<this.properties.length; t++) {
		var p = this.properties[t];
		if(p.atEnd !== undefined) {
			this.assignStyle(this.element,p.style,p.atEnd);
		}
	}
	if(this.callback)
		this.callback(this.element,this.properties);
};

Morpher.prototype.tick = function()
{
	var currTime = Number(new Date());
	var progress = Animator.slowInSlowOut(Math.min(1,(currTime-this.startTime)/this.duration));
	for(var t=0; t<this.properties.length; t++) {
		var p = this.properties[t];
		if(p.start !== undefined && p.end !== undefined) {
			var template = p.template || "%0";
			switch(p.format) {
			case undefined:
			case "style":
				var v = p.start + (p.end-p.start) * progress;
				this.assignStyle(this.element,p.style,template.format([v]));
				break;
			case "color":
				break;
			}
		}
	}
	if(currTime >= this.endTime) {
		this.stop();
		return false;
	}
	return true;
};

//--
//-- Zoomer animation
//--

function Zoomer(text,startElement,targetElement,unused)
{
	var e = createTiddlyElement(document.body,"div",null,"zoomer");
	createTiddlyElement(e,"div",null,null,text);
	var winWidth = findWindowWidth();
	var winHeight = findWindowHeight();
	var p = [
		{style: 'left', start: findPosX(startElement), end: findPosX(targetElement), template: '%0px'},
		{style: 'top', start: findPosY(startElement), end: findPosY(targetElement), template: '%0px'},
		{style: 'width', start: Math.min(startElement.scrollWidth,winWidth), end: Math.min(targetElement.scrollWidth,winWidth), template: '%0px', atEnd: 'auto'},
		{style: 'height', start: Math.min(startElement.scrollHeight,winHeight), end: Math.min(targetElement.scrollHeight,winHeight), template: '%0px', atEnd: 'auto'},
		{style: 'fontSize', start: 8, end: 24, template: '%0pt'}
	];
	var c = function(element,properties) {removeNode(element);};
	return new Morpher(e,config.animDuration,p,c);
}

//--
//-- Scroller animation
//--

function Scroller(targetElement)
{
	var p = [{style: '-tw-vertScroll', start: findScrollY(), end: ensureVisible(targetElement)}];
	return new Morpher(targetElement,config.animDuration,p);
}

//--
//-- Slider animation
//--

// deleteMode - "none", "all" [delete target element and it's children], [only] "children" [but not the target element]
function Slider(element,opening,unused,deleteMode)
{
	element.style.overflow = 'hidden';
	if(opening)
		element.style.height = '0px'; // Resolves a Firefox flashing bug
	element.style.display = 'block';
	var left = findPosX(element);
	var width = element.scrollWidth;
	var height = element.scrollHeight;
	var winWidth = findWindowWidth();
	var p = [];
	var c = null;
	if(opening) {
		p.push({style: 'height', start: 0, end: height, template: '%0px', atEnd: 'auto'});
		p.push({style: 'opacity', start: 0, end: 1, template: '%0'});
		p.push({style: 'filter', start: 0, end: 100, template: 'alpha(opacity:%0)'});
	} else {
		p.push({style: 'height', start: height, end: 0, template: '%0px'});
		p.push({style: 'display', atEnd: 'none'});
		p.push({style: 'opacity', start: 1, end: 0, template: '%0'});
		p.push({style: 'filter', start: 100, end: 0, template: 'alpha(opacity:%0)'});
		switch(deleteMode) {
		case "all":
			c = function(element,properties) {removeNode(element);};
			break;
		case "children":
			c = function(element,properties) {removeChildren(element);};
			break;
		}
	}
	return new Morpher(element,config.animDuration,p,c);
}

//--
//-- Popup menu
//--

var Popup = {
	stack: [] // Array of objects with members root: and popup:
	};

Popup.create = function(root,elem,className)
{
	var stackPosition = this.find(root,"popup");
	Popup.remove(stackPosition+1);
	var popup = createTiddlyElement(document.body,elem || "ol","popup",className || "popup");
	popup.stackPosition = stackPosition;
	Popup.stack.push({root: root, popup: popup});
	return popup;
};

Popup.onDocumentClick = function(ev)
{
	var e = ev || window.event;
	if(e.eventPhase == undefined)
		Popup.remove();
	else if(e.eventPhase == Event.BUBBLING_PHASE || e.eventPhase == Event.AT_TARGET)
		Popup.remove();
	return true;
};

Popup.show = function(valign,halign,offset)
{
	var curr = Popup.stack[Popup.stack.length-1];
	this.place(curr.root,curr.popup,valign,halign,offset);
	addClass(curr.root,"highlight");
	if(config.options.chkAnimate && anim && typeof Scroller == "function")
		anim.startAnimating(new Scroller(curr.popup));
	else
		window.scrollTo(0,ensureVisible(curr.popup));
};

Popup.place = function(root,popup,valign,halign,offset)
{
	if(!offset)
		var offset = {x:0,y:0};
	if(popup.stackPosition >= 0 && !valign && !halign) {
		offset.x = offset.x + root.offsetWidth;
	} else {
		offset.x = (halign == "right") ? offset.x + root.offsetWidth : offset.x;
		offset.y = (valign == "top") ? offset.y : offset.y + root.offsetHeight;
	}
	var rootLeft = findPosX(root);
	var rootTop = findPosY(root);
	var popupLeft = rootLeft + offset.x;
	var popupTop = rootTop + offset.y;
	var winWidth = findWindowWidth();
	if(popup.offsetWidth > winWidth*0.75)
		popup.style.width = winWidth*0.75 + "px";
	var popupWidth = popup.offsetWidth;
	var scrollWidth = winWidth - document.body.offsetWidth;
	if(popupLeft + popupWidth > winWidth - scrollWidth - 1) {
		if(halign == "right")
			popupLeft = popupLeft - root.offsetWidth - popupWidth;
		else
			popupLeft = winWidth - popupWidth - scrollWidth - 1;
	}
	popup.style.left = popupLeft + "px";
	popup.style.top = popupTop + "px";
	popup.style.display = "block";
};

Popup.find = function(e)
{
	var pos = -1;
	for (var t=this.stack.length-1; t>=0; t--) {
		if(isDescendant(e,this.stack[t].popup))
			pos = t;
	}
	return pos;
};

Popup.remove = function(pos)
{
	if(!pos) var pos = 0;
	if(Popup.stack.length > pos) {
		Popup.removeFrom(pos);
	}
};

Popup.removeFrom = function(from)
{
	for(var t=Popup.stack.length-1; t>=from; t--) {
		var p = Popup.stack[t];
		removeClass(p.root,"highlight");
		removeNode(p.popup);
	}
	Popup.stack = Popup.stack.slice(0,from);
};

//--
//-- Wizard support
//--

function Wizard(elem)
{
	if(elem) {
		this.formElem = findRelated(elem,"wizard","className");
		this.bodyElem = findRelated(this.formElem.firstChild,"wizardBody","className","nextSibling");
		this.footElem = findRelated(this.formElem.firstChild,"wizardFooter","className","nextSibling");
	} else {
		this.formElem = null;
		this.bodyElem = null;
		this.footElem = null;
	}
}

Wizard.prototype.setValue = function(name,value)
{
	if(this.formElem)
		this.formElem[name] = value;
};

Wizard.prototype.getValue = function(name)
{
	return this.formElem ? this.formElem[name] : null;
};

Wizard.prototype.createWizard = function(place,title)
{
	this.formElem = createTiddlyElement(place,"form",null,"wizard");
	createTiddlyElement(this.formElem,"h1",null,null,title);
	this.bodyElem = createTiddlyElement(this.formElem,"div",null,"wizardBody");
	this.footElem = createTiddlyElement(this.formElem,"div",null,"wizardFooter");
	return this.formElem;
};

Wizard.prototype.clear = function()
{
	removeChildren(this.bodyElem);
};

Wizard.prototype.setButtons = function(buttonInfo,status)
{
	removeChildren(this.footElem);
	for(var t=0; t<buttonInfo.length; t++) {
		createTiddlyButton(this.footElem,buttonInfo[t].caption,buttonInfo[t].tooltip,buttonInfo[t].onClick);
		insertSpacer(this.footElem);
		}
	if(typeof status == "string") {
		createTiddlyElement(this.footElem,"span",null,"status",status);
	}
};

Wizard.prototype.addStep = function(stepTitle,html)
{
	removeChildren(this.bodyElem);
	var w = createTiddlyElement(this.bodyElem,"div");
	createTiddlyElement(w,"h2",null,null,stepTitle);
	var step = createTiddlyElement(w,"div",null,"wizardStep");
	step.innerHTML = html;
	applyHtmlMacros(step,tiddler);
};

Wizard.prototype.getElement = function(name)
{
	return this.formElem.elements[name];
};

//--
//-- ListView gadget
//--

var ListView = {};

// Create a listview
ListView.create = function(place,listObject,listTemplate,callback,className)
{
	var table = createTiddlyElement(place,"table",null,className || "listView twtable");
	var thead = createTiddlyElement(table,"thead");
	var r = createTiddlyElement(thead,"tr");
	for(var t=0; t<listTemplate.columns.length; t++) {
		var columnTemplate = listTemplate.columns[t];
		var c = createTiddlyElement(r,"th");
		var colType = ListView.columnTypes[columnTemplate.type];
		if(colType && colType.createHeader) {
			colType.createHeader(c,columnTemplate,t);
			if(columnTemplate.className)
				addClass(c,columnTemplate.className);
		}
	}
	var tbody = createTiddlyElement(table,"tbody");
	for(var rc=0; rc<listObject.length; rc++) {
		var rowObject = listObject[rc];
		r = createTiddlyElement(tbody,"tr");
		for(c=0; c<listTemplate.rowClasses.length; c++) {
			if(rowObject[listTemplate.rowClasses[c].field])
				addClass(r,listTemplate.rowClasses[c].className);
		}
		rowObject.rowElement = r;
		rowObject.colElements = {};
		for(var cc=0; cc<listTemplate.columns.length; cc++) {
			c = createTiddlyElement(r,"td");
			columnTemplate = listTemplate.columns[cc];
			var field = columnTemplate.field;
			colType = ListView.columnTypes[columnTemplate.type];
			if(colType && colType.createItem) {
				colType.createItem(c,rowObject,field,columnTemplate,cc,rc);
				if(columnTemplate.className)
					addClass(c,columnTemplate.className);
			}
			rowObject.colElements[field] = c;
		}
	}
	if(callback && listTemplate.actions)
		createTiddlyDropDown(place,ListView.getCommandHandler(callback),listTemplate.actions);
	if(callback && listTemplate.buttons) {
		for(t=0; t<listTemplate.buttons.length; t++) {
			var a = listTemplate.buttons[t];
			if(a && a.name != "")
				createTiddlyButton(place,a.caption,null,ListView.getCommandHandler(callback,a.name,a.allowEmptySelection));
		}
	}
	return table;
};

ListView.getCommandHandler = function(callback,name,allowEmptySelection)
{
	return function(e) {
		var view = findRelated(this,"TABLE",null,"previousSibling");
		var tiddlers = [];
		ListView.forEachSelector(view,function(e,rowName) {
					if(e.checked)
						tiddlers.push(rowName);
					});
		if(tiddlers.length == 0 && !allowEmptySelection) {
			alert(config.messages.nothingSelected);
		} else {
			if(this.nodeName.toLowerCase() == "select") {
				callback(view,this.value,tiddlers);
				this.selectedIndex = 0;
			} else {
				callback(view,name,tiddlers);
			}
		}
	};
};

// Invoke a callback for each selector checkbox in the listview
ListView.forEachSelector = function(view,callback)
{
	var checkboxes = view.getElementsByTagName("input");
	var hadOne = false;
	for(var t=0; t<checkboxes.length; t++) {
		var cb = checkboxes[t];
		if(cb.getAttribute("type") == "checkbox") {
			var rn = cb.getAttribute("rowName");
			if(rn) {
				callback(cb,rn);
				hadOne = true;
			}
		}
	}
	return hadOne;
};

ListView.getSelectedRows = function(view)
{
	var rowNames = [];
	ListView.forEachSelector(view,function(e,rowName) {
				if(e.checked)
					rowNames.push(rowName);
				});
	return rowNames;
};

ListView.columnTypes = {};

ListView.columnTypes.String = {
	createHeader: function(place,columnTemplate,col)
		{
			createTiddlyText(place,columnTemplate.title);
		},
	createItem: function(place,listObject,field,columnTemplate,col,row)
		{
			var v = listObject[field];
			if(v != undefined)
				createTiddlyText(place,v);
		}
};

ListView.columnTypes.WikiText = {
	createHeader: ListView.columnTypes.String.createHeader,
	createItem: function(place,listObject,field,columnTemplate,col,row)
		{
			var v = listObject[field];
			if(v != undefined)
				wikify(v,place,null,null);
		}
};

ListView.columnTypes.Tiddler = {
	createHeader: ListView.columnTypes.String.createHeader,
	createItem: function(place,listObject,field,columnTemplate,col,row)
		{
			var v = listObject[field];
			if(v != undefined && v.title)
				createTiddlyPopup(place,v.title,config.messages.listView.tiddlerTooltip,v);
		}
};

ListView.columnTypes.Size = {
	createHeader: ListView.columnTypes.String.createHeader,
	createItem: function(place,listObject,field,columnTemplate,col,row)
		{
			var msg = config.messages.sizeTemplates;
			var v = listObject[field];
			if(v != undefined) {
				var t = 0;
				while(t<msg.length-1 && v<msg[t].unit)
					t++;
				createTiddlyText(place,msg[t].template.format([Math.round(v/msg[t].unit)]));
			}
		}
};

ListView.columnTypes.Link = {
	createHeader: ListView.columnTypes.String.createHeader,
	createItem: function(place,listObject,field,columnTemplate,col,row)
		{
			var v = listObject[field];
			var c = columnTemplate.text;
			if(v != undefined)
				createExternalLink(place,v,c || v);
		}
};

ListView.columnTypes.Date = {
	createHeader: ListView.columnTypes.String.createHeader,
	createItem: function(place,listObject,field,columnTemplate,col,row)
		{
			var v = listObject[field];
			if(v != undefined)
				createTiddlyText(place,v.formatString(columnTemplate.dateFormat));
		}
};

ListView.columnTypes.StringList = {
	createHeader: ListView.columnTypes.String.createHeader,
	createItem: function(place,listObject,field,columnTemplate,col,row)
		{
			var v = listObject[field];
			if(v != undefined) {
				for(var t=0; t<v.length; t++) {
					createTiddlyText(place,v[t]);
					createTiddlyElement(place,"br");
				}
			}
		}
};

ListView.columnTypes.Selector = {
	createHeader: function(place,columnTemplate,col)
		{
			createTiddlyCheckbox(place,null,false,this.onHeaderChange);
		},
	createItem: function(place,listObject,field,columnTemplate,col,row)
		{
			var e = createTiddlyCheckbox(place,null,listObject[field],null);
			e.setAttribute("rowName",listObject[columnTemplate.rowName]);
		},
	onHeaderChange: function(e)
		{
			var state = this.checked;
			var view = findRelated(this,"TABLE");
			if(!view)
				return;
			ListView.forEachSelector(view,function(e,rowName) {
								e.checked = state;
							});
		}
};

ListView.columnTypes.Tags = {
	createHeader: ListView.columnTypes.String.createHeader,
	createItem: function(place,listObject,field,columnTemplate,col,row)
		{
			var tags = listObject[field];
			createTiddlyText(place,String.encodeTiddlyLinkList(tags));
		}
};

ListView.columnTypes.Boolean = {
	createHeader: ListView.columnTypes.String.createHeader,
	createItem: function(place,listObject,field,columnTemplate,col,row)
		{
			if(listObject[field] == true)
				createTiddlyText(place,columnTemplate.trueText);
			if(listObject[field] == false)
				createTiddlyText(place,columnTemplate.falseText);
		}
};

ListView.columnTypes.TagCheckbox = {
	createHeader: ListView.columnTypes.String.createHeader,
	createItem: function(place,listObject,field,columnTemplate,col,row)
		{
			var e = createTiddlyCheckbox(place,null,listObject[field],this.onChange);
			e.setAttribute("tiddler",listObject.title);
			e.setAttribute("tag",columnTemplate.tag);
		},
	onChange : function(e)
		{
			var tag = this.getAttribute("tag");
			var tiddler = this.getAttribute("tiddler");
			store.setTiddlerTag(tiddler,this.checked,tag);
		}
};

ListView.columnTypes.TiddlerLink = {
	createHeader: ListView.columnTypes.String.createHeader,
	createItem: function(place,listObject,field,columnTemplate,col,row)
		{
			var v = listObject[field];
			if(v != undefined) {
				var link = createTiddlyLink(place,listObject[columnTemplate.tiddlerLink],false,null);
				createTiddlyText(link,listObject[field]);
			}
		}
};

//--
//-- Augmented methods for the JavaScript Number(), Array(), String() and Date() objects
//--

// Clamp a number to a range
Number.prototype.clamp = function(min,max)
{
	var c = this;
	if(c < min)
		c = min;
	if(c > max)
		c = max;
	return Number(c);
};

// Add indexOf function if browser does not support it
if(!Array.indexOf) {
Array.prototype.indexOf = function(item,from)
{
	if(!from)
		from = 0;
	for(var i=from; i<this.length; i++) {
		if(this[i] === item)
			return i;
	}
	return -1;
};}

// Find an entry in a given field of the members of an array
Array.prototype.findByField = function(field,value)
{
	for(var t=0; t<this.length; t++) {
		if(this[t][field] === value)
			return t;
	}
	return null;
};

// Return whether an entry exists in an array
Array.prototype.contains = function(item)
{
	return this.indexOf(item) != -1;
};

// Adds, removes or toggles a particular value within an array
//  value - value to add
//  mode - +1 to add value, -1 to remove value, 0 to toggle it
Array.prototype.setItem = function(value,mode)
{
	var p = this.indexOf(value);
	if(mode == 0)
		mode = (p == -1) ? +1 : -1;
	if(mode == +1) {
		if(p == -1)
			this.push(value);
	} else if(mode == -1) {
		if(p != -1)
			this.splice(p,1);
	}
};

// Return whether one of a list of values exists in an array
Array.prototype.containsAny = function(items)
{
	for(var i=0; i<items.length; i++) {
		if(this.indexOf(items[i]) != -1)
			return true;
	}
	return false;
};

// Return whether all of a list of values exists in an array
Array.prototype.containsAll = function(items)
{
	for(var i = 0; i<items.length; i++) {
		if(this.indexOf(items[i]) == -1)
			return false;
	}
	return true;
};

// Push a new value into an array only if it is not already present in the array. If the optional unique parameter is false, it reverts to a normal push
Array.prototype.pushUnique = function(item,unique)
{
	if(unique === false) {
		this.push(item);
	} else {
		if(this.indexOf(item) == -1)
			this.push(item);
	}
};

Array.prototype.remove = function(item)
{
	var p = this.indexOf(item);
	if(p != -1)
		this.splice(p,1);
};

if(!Array.prototype.map) {
Array.prototype.map = function(fn,thisObj)
{
	var scope = thisObj || window;
	var a = [];
	for(var i=0, j=this.length; i < j; ++i) {
		a.push(fn.call(scope,this[i],i,this));
	}
	return a;
};}

// Get characters from the right end of a string
String.prototype.right = function(n)
{
	return n < this.length ? this.slice(this.length-n) : this;
};

// Trim whitespace from both ends of a string
String.prototype.trim = function()
{
	return this.replace(/^\s*|\s*$/g,"");
};

// Convert a string from a CSS style property name to a JavaScript style name ("background-color" -> "backgroundColor")
String.prototype.unDash = function()
{
	var s = this.split("-");
	if(s.length > 1) {
		for(var t=1; t<s.length; t++)
			s[t] = s[t].substr(0,1).toUpperCase() + s[t].substr(1);
	}
	return s.join("");
};

// Substitute substrings from an array into a format string that includes '%1'-type specifiers
String.prototype.format = function(s)
{
	var substrings = s && s.constructor == Array ? s : arguments;
	var subRegExp = /(?:%(\d+))/mg;
	var currPos = 0;
	var r = [];
	do {
		var match = subRegExp.exec(this);
		if(match && match[1]) {
			if(match.index > currPos)
				r.push(this.substring(currPos,match.index));
			r.push(substrings[parseInt(match[1])]);
			currPos = subRegExp.lastIndex;
		}
	} while(match);
	if(currPos < this.length)
		r.push(this.substring(currPos,this.length));
	return r.join("");
};

// Escape any special RegExp characters with that character preceded by a backslash
String.prototype.escapeRegExp = function()
{
	var s = "\\^$*+?()=!|,{}[].";
	var c = this;
	for(var t=0; t<s.length; t++)
		c = c.replace(new RegExp("\\" + s.substr(t,1),"g"),"\\" + s.substr(t,1));
	return c;
};

// Convert "\" to "\s", newlines to "\n" (and remove carriage returns)
String.prototype.escapeLineBreaks = function()
{
	return this.replace(/\\/mg,"\\s").replace(/\n/mg,"\\n").replace(/\r/mg,"");
};

// Convert "\n" to newlines, "\b" to " ", "\s" to "\" (and remove carriage returns)
String.prototype.unescapeLineBreaks = function()
{
	return this.replace(/\\n/mg,"\n").replace(/\\b/mg," ").replace(/\\s/mg,"\\").replace(/\r/mg,"");
};

// Convert & to "&amp;", < to "&lt;", > to "&gt;" and " to "&quot;"
String.prototype.htmlEncode = function()
{
	return this.replace(/&/mg,"&amp;").replace(/</mg,"&lt;").replace(/>/mg,"&gt;").replace(/\"/mg,"&quot;");
};

// Convert "&amp;" to &, "&lt;" to <, "&gt;" to > and "&quot;" to "
String.prototype.htmlDecode = function()
{
	return this.replace(/&lt;/mg,"<").replace(/&gt;/mg,">").replace(/&quot;/mg,"\"").replace(/&amp;/mg,"&");
};

// Parse a space-separated string of name:value parameters
// The result is an array of objects:
//   result[0] = object with a member for each parameter name, value of that member being an array of values
//   result[1..n] = one object for each parameter, with 'name' and 'value' members
String.prototype.parseParams = function(defaultName,defaultValue,allowEval,noNames,cascadeDefaults)
{
	var parseToken = function(match,p) {
		var n;
		if(match[p]) // Double quoted
			n = match[p];
		else if(match[p+1]) // Single quoted
			n = match[p+1];
		else if(match[p+2]) // Double-square-bracket quoted
			n = match[p+2];
		else if(match[p+3]) // Double-brace quoted
			try {
				n = match[p+3];
				if(allowEval && config.evaluateMacroParameters != "none") {
					if(config.evaluateMacroParameters == "restricted") {
						if(window.restrictedEval) {
							n = window.restrictedEval(n);
						}
					} else {
						n = window.eval(n);
					}
				}
			} catch(ex) {
				throw "Unable to evaluate {{" + match[p+3] + "}}: " + exceptionText(ex);
			}
		else if(match[p+4]) // Unquoted
			n = match[p+4];
		else if(match[p+5]) // empty quote
			n = "";
		return n;
	};
	var r = [{}];
	var dblQuote = "(?:\"((?:(?:\\\\\")|[^\"])+)\")";
	var sngQuote = "(?:'((?:(?:\\\\\')|[^'])+)')";
	var dblSquare = "(?:\\[\\[((?:\\s|\\S)*?)\\]\\])";
	var dblBrace = "(?:\\{\\{((?:\\s|\\S)*?)\\}\\})";
	var unQuoted = noNames ? "([^\"'\\s]\\S*)" : "([^\"':\\s][^\\s:]*)";
	var emptyQuote = "((?:\"\")|(?:''))";
	var skipSpace = "(?:\\s*)";
	var token = "(?:" + dblQuote + "|" + sngQuote + "|" + dblSquare + "|" + dblBrace + "|" + unQuoted + "|" + emptyQuote + ")";
	var re = noNames ? new RegExp(token,"mg") : new RegExp(skipSpace + token + skipSpace + "(?:(\\:)" + skipSpace + token + ")?","mg");
	var params = [];
	do {
		var match = re.exec(this);
		if(match) {
			var n = parseToken(match,1);
			if(noNames) {
				r.push({name:"",value:n});
			} else {
				var v = parseToken(match,8);
				if(v == null && defaultName) {
					v = n;
					n = defaultName;
				} else if(v == null && defaultValue) {
					v = defaultValue;
				}
				r.push({name:n,value:v});
				if(cascadeDefaults) {
					defaultName = n;
					defaultValue = v;
				}
			}
		}
	} while(match);
	// Summarise parameters into first element
	for(var t=1; t<r.length; t++) {
		if(r[0][r[t].name])
			r[0][r[t].name].push(r[t].value);
		else
			r[0][r[t].name] = [r[t].value];
	}
	return r;
};

// Process a string list of macro parameters into an array. Parameters can be quoted with "", '',
// [[]], {{ }} or left unquoted (and therefore space-separated). Double-braces {{}} results in
// an *evaluated* parameter: e.g. {{config.options.txtUserName}} results in the current user's name.
String.prototype.readMacroParams = function(notAllowEval)
{
	var p = this.parseParams("list",null,!notAllowEval,true);
	var n = [];
	for(var t=1; t<p.length; t++)
		n.push(p[t].value);
	return n;
};

// Process a string list of unique tiddler names into an array. Tiddler names that have spaces in them must be [[bracketed]]
String.prototype.readBracketedList = function(unique)
{
	var p = this.parseParams("list",null,false,true);
	var n = [];
	for(var t=1; t<p.length; t++) {
		if(p[t].value)
			n.pushUnique(p[t].value,unique);
	}
	return n;
};

// Returns array with start and end index of chunk between given start and end marker, or undefined.
String.prototype.getChunkRange = function(start,end)
{
	var s = this.indexOf(start);
	if(s != -1) {
		s += start.length;
		var e = this.indexOf(end,s);
		if(e != -1)
			return [s,e];
	}
};

// Replace a chunk of a string given start and end markers
String.prototype.replaceChunk = function(start,end,sub)
{
	var r = this.getChunkRange(start,end);
	return r ? this.substring(0,r[0]) + sub + this.substring(r[1]) : this;
};

// Returns a chunk of a string between start and end markers, or undefined
String.prototype.getChunk = function(start,end)
{
	var r = this.getChunkRange(start,end);
	if(r)
		return this.substring(r[0],r[1]);
};


// Static method to bracket a string with double square brackets if it contains a space
String.encodeTiddlyLink = function(title)
{
	return title.indexOf(" ") == -1 ? title : "[[" + title + "]]";
};

// Static method to encodeTiddlyLink for every item in an array and join them with spaces
String.encodeTiddlyLinkList = function(list)
{
	if(list) {
		var results = [];
		for(var t=0; t<list.length; t++)
			results.push(String.encodeTiddlyLink(list[t]));
		return results.join(" ");
	} else {
		return "";
	}
};

// Convert a string as a sequence of name:"value" pairs into a hashmap
String.prototype.decodeHashMap = function()
{
	var fields = this.parseParams("anon","",false);
	var r = {};
	for(var t=1; t<fields.length; t++)
		r[fields[t].name] = fields[t].value;
	return r;
};

// Static method to encode a hashmap into a name:"value"... string
String.encodeHashMap = function(hashmap)
{
	var r = [];
	for(var t in hashmap)
		r.push(t + ':"' + hashmap[t] + '"');
	return r.join(" ");
};

// Static method to left-pad a string with 0s to a certain width
String.zeroPad = function(n,d)
{
	var s = n.toString();
	if(s.length < d)
		s = "000000000000000000000000000".substr(0,d-s.length) + s;
	return s;
};

String.prototype.startsWith = function(prefix)
{
	return !prefix || this.substring(0,prefix.length) == prefix;
};

// Returns the first value of the given named parameter.
function getParam(params,name,defaultValue)
{
	if(!params)
		return defaultValue;
	var p = params[0][name];
	return p ? p[0] : defaultValue;
}

// Returns the first value of the given boolean named parameter.
function getFlag(params,name,defaultValue)
{
	return !!getParam(params,name,defaultValue);
}

// Substitute date components into a string
Date.prototype.formatString = function(template)
{
	var t = template.replace(/0hh12/g,String.zeroPad(this.getHours12(),2));
	t = t.replace(/hh12/g,this.getHours12());
	t = t.replace(/0hh/g,String.zeroPad(this.getHours(),2));
	t = t.replace(/hh/g,this.getHours());
	t = t.replace(/mmm/g,config.messages.dates.shortMonths[this.getMonth()]);
	t = t.replace(/0mm/g,String.zeroPad(this.getMinutes(),2));
	t = t.replace(/mm/g,this.getMinutes());
	t = t.replace(/0ss/g,String.zeroPad(this.getSeconds(),2));
	t = t.replace(/ss/g,this.getSeconds());
	t = t.replace(/[ap]m/g,this.getAmPm().toLowerCase());
	t = t.replace(/[AP]M/g,this.getAmPm().toUpperCase());
	t = t.replace(/wYYYY/g,this.getYearForWeekNo());
	t = t.replace(/wYY/g,String.zeroPad(this.getYearForWeekNo()-2000,2));
	t = t.replace(/YYYY/g,this.getFullYear());
	t = t.replace(/YY/g,String.zeroPad(this.getFullYear()-2000,2));
	t = t.replace(/MMM/g,config.messages.dates.months[this.getMonth()]);
	t = t.replace(/0MM/g,String.zeroPad(this.getMonth()+1,2));
	t = t.replace(/MM/g,this.getMonth()+1);
	t = t.replace(/0WW/g,String.zeroPad(this.getWeek(),2));
	t = t.replace(/WW/g,this.getWeek());
	t = t.replace(/DDD/g,config.messages.dates.days[this.getDay()]);
	t = t.replace(/ddd/g,config.messages.dates.shortDays[this.getDay()]);
	t = t.replace(/0DD/g,String.zeroPad(this.getDate(),2));
	t = t.replace(/DDth/g,this.getDate()+this.daySuffix());
	t = t.replace(/DD/g,this.getDate());
	var tz = this.getTimezoneOffset();
	var atz = Math.abs(tz);
	t = t.replace(/TZD/g,(tz < 0 ? '+' : '-') + String.zeroPad(Math.floor(atz / 60),2) + ':' + String.zeroPad(atz % 60,2));
	t = t.replace(/\\/g,"");
	return t;
};

Date.prototype.getWeek = function()
{
	var dt = new Date(this.getTime());
	var d = dt.getDay();
	if(d==0) d=7;// JavaScript Sun=0, ISO Sun=7
	dt.setTime(dt.getTime()+(4-d)*86400000);// shift day to Thurs of same week to calculate weekNo
	var n = Math.floor((dt.getTime()-new Date(dt.getFullYear(),0,1)+3600000)/86400000);
	return Math.floor(n/7)+1;
};

Date.prototype.getYearForWeekNo = function()
{
	var dt = new Date(this.getTime());
	var d = dt.getDay();
	if(d==0) d=7;// JavaScript Sun=0, ISO Sun=7
	dt.setTime(dt.getTime()+(4-d)*86400000);// shift day to Thurs of same week
	return dt.getFullYear();
};

Date.prototype.getHours12 = function()
{
	var h = this.getHours();
	return h > 12 ? h-12 : ( h > 0 ? h : 12 );
};

Date.prototype.getAmPm = function()
{
	return this.getHours() >= 12 ? config.messages.dates.pm : config.messages.dates.am;
};

Date.prototype.daySuffix = function()
{
	return config.messages.dates.daySuffixes[this.getDate()-1];
};

// Convert a date to local YYYYMMDDHHMM string format
Date.prototype.convertToLocalYYYYMMDDHHMM = function()
{
	return this.getFullYear() + String.zeroPad(this.getMonth()+1,2) + String.zeroPad(this.getDate(),2) + String.zeroPad(this.getHours(),2) + String.zeroPad(this.getMinutes(),2);
};

// Convert a date to UTC YYYYMMDDHHMM string format
Date.prototype.convertToYYYYMMDDHHMM = function()
{
	return this.getUTCFullYear() + String.zeroPad(this.getUTCMonth()+1,2) + String.zeroPad(this.getUTCDate(),2) + String.zeroPad(this.getUTCHours(),2) + String.zeroPad(this.getUTCMinutes(),2);
};

// Convert a date to UTC YYYYMMDD.HHMMSSMMM string format
Date.prototype.convertToYYYYMMDDHHMMSSMMM = function()
{
	return this.getUTCFullYear() + String.zeroPad(this.getUTCMonth()+1,2) + String.zeroPad(this.getUTCDate(),2) + "." + String.zeroPad(this.getUTCHours(),2) + String.zeroPad(this.getUTCMinutes(),2) + String.zeroPad(this.getUTCSeconds(),2) + String.zeroPad(this.getUTCMilliseconds(),3) +"0";
};

// Static method to create a date from a UTC YYYYMMDDHHMM format string
Date.convertFromYYYYMMDDHHMM = function(d)
{
	d = d?d.replace(/[^0-9]/g, ""):"";
	return Date.convertFromYYYYMMDDHHMMSSMMM(d.substr(0,12));
};

// Static method to create a date from a UTC YYYYMMDDHHMMSS format string
Date.convertFromYYYYMMDDHHMMSS = function(d)
{
	d = d?d.replace(/[^0-9]/g, ""):"";
	return Date.convertFromYYYYMMDDHHMMSSMMM(d.substr(0,14));
};

// Static method to create a date from a UTC YYYYMMDDHHMMSSMMM format string
Date.convertFromYYYYMMDDHHMMSSMMM = function(d)
{
	d = d ? d.replace(/[^0-9]/g, "") : "";
	return new Date(Date.UTC(parseInt(d.substr(0,4),10),
			parseInt(d.substr(4,2),10)-1,
			parseInt(d.substr(6,2),10),
			parseInt(d.substr(8,2)||"00",10),
			parseInt(d.substr(10,2)||"00",10),
			parseInt(d.substr(12,2)||"00",10),
			parseInt(d.substr(14,3)||"000",10)));
};

//--
//-- RGB colour object
//--

// Construct an RGB colour object from a '#rrggbb', '#rgb' or 'rgb(n,n,n)' string or from separate r,g,b values
function RGB(r,g,b)
{
	this.r = 0;
	this.g = 0;
	this.b = 0;
	if(typeof r == "string") {
		if(r.substr(0,1) == "#") {
			if(r.length == 7) {
				this.r = parseInt(r.substr(1,2),16)/255;
				this.g = parseInt(r.substr(3,2),16)/255;
				this.b = parseInt(r.substr(5,2),16)/255;
			} else {
				this.r = parseInt(r.substr(1,1),16)/15;
				this.g = parseInt(r.substr(2,1),16)/15;
				this.b = parseInt(r.substr(3,1),16)/15;
			}
		} else {
			var rgbPattern = /rgb\s*\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)/;
			var c = r.match(rgbPattern);
			if(c) {
				this.r = parseInt(c[1],10)/255;
				this.g = parseInt(c[2],10)/255;
				this.b = parseInt(c[3],10)/255;
			}
		}
	} else {
		this.r = r;
		this.g = g;
		this.b = b;
	}
	return this;
}

// Mixes this colour with another in a specified proportion
// c = other colour to mix
// f = 0..1 where 0 is this colour and 1 is the new colour
// Returns an RGB object
RGB.prototype.mix = function(c,f)
{
	return new RGB(this.r + (c.r-this.r) * f,this.g + (c.g-this.g) * f,this.b + (c.b-this.b) * f);
};

// Return an rgb colour as a #rrggbb format hex string
RGB.prototype.toString = function()
{
	return "#" + ("0" + Math.floor(this.r.clamp(0,1) * 255).toString(16)).right(2) +
				("0" + Math.floor(this.g.clamp(0,1) * 255).toString(16)).right(2) +
				("0" + Math.floor(this.b.clamp(0,1) * 255).toString(16)).right(2);
};

//--
//-- DOM utilities - many derived from www.quirksmode.org
//--

// Resolve the target object of an event
function resolveTarget(e)
{
	var obj;
	if(e.target)
		obj = e.target;
	else if(e.srcElement)
		obj = e.srcElement;
	if(obj.nodeType == 3) // defeat Safari bug
		obj = obj.parentNode;
	return obj;
}

function drawGradient(place,horiz,locolors,hicolors)
{
	if(!hicolors)
		hicolors = locolors;
	for(var t=0; t<= 100; t+=2) {
		var bar = document.createElement("div");
		place.appendChild(bar);
		bar.style.position = "absolute";
		bar.style.left = horiz ? t + "%" : 0;
		bar.style.top = horiz ? 0 : t + "%";
		bar.style.width = horiz ? (101-t) + "%" : "100%";
		bar.style.height = horiz ? "100%" : (101-t) + "%";
		bar.style.zIndex = -1;
		var p = t/100*(locolors.length-1);
		var hc = hicolors[Math.floor(p)];
		if(typeof hc == "string")
			hc = new RGB(hc);
		var lc = locolors[Math.ceil(p)];
		if(typeof lc == "string")
			lc = new RGB(lc);
		bar.style.backgroundColor = hc.mix(lc,p-Math.floor(p)).toString();
	}
}

function createTiddlyText(parent,text)
{
	return parent.appendChild(document.createTextNode(text));
}

function createTiddlyCheckbox(parent,caption,checked,onChange)
{
	var cb = document.createElement("input");
	cb.setAttribute("type","checkbox");
	cb.onclick = onChange;
	parent.appendChild(cb);
	cb.checked = checked;
	cb.className = "chkOptionInput";
	if(caption)
		wikify(caption,parent);
	return cb;
}

function createTiddlyElement(parent,element,id,className,text,attribs)
{
	var e = document.createElement(element);
	if(className != null)
		e.className = className;
	if(id != null)
		e.setAttribute("id",id);
	if(text != null)
		e.appendChild(document.createTextNode(text));
	if(attribs) {
		for(var n in attribs) {
			e.setAttribute(n,attribs[n]);
		}
	}
	if(parent != null)
		parent.appendChild(e);
	return e;
}

function addEvent(obj,type,fn)
{
	if(obj.attachEvent) {
		obj["e"+type+fn] = fn;
		obj[type+fn] = function(){obj["e"+type+fn](window.event);};
		obj.attachEvent("on"+type,obj[type+fn]);
	} else {
		obj.addEventListener(type,fn,false);
	}
}

function removeEvent(obj,type,fn)
{
	if(obj.detachEvent) {
		obj.detachEvent("on"+type,obj[type+fn]);
		obj[type+fn] = null;
	} else {
		obj.removeEventListener(type,fn,false);
	}
}


// Find the closest relative with a given property value (property defaults to tagName, relative defaults to parentNode)
function findRelated(e,value,name,relative)
{
	name = name || "tagName";
	relative = relative || "parentNode";
	if(name == "className") {
		while(e && !hasClass(e,value)) {
			e = e[relative];
		}
	} else {
		while(e && e[name] != value) {
			e = e[relative];
		}
	}
	return e;
}

// Get the scroll position for window.scrollTo necessary to scroll a given element into view
function ensureVisible(e)
{
	var posTop = findPosY(e);
	var posBot = posTop + e.offsetHeight;
	var winTop = findScrollY();
	var winHeight = findWindowHeight();
	var winBot = winTop + winHeight;
	if(posTop < winTop) {
		return posTop;
	} else if(posBot > winBot) {
		if(e.offsetHeight < winHeight)
			return posTop - (winHeight - e.offsetHeight);
		else
			return posTop;
	} else {
		return winTop;
	}
}

// Get the current width of the display window
function findWindowWidth()
{
	return window.innerWidth || document.documentElement.clientWidth;
}

// Get the current height of the display window
function findWindowHeight()
{
	return window.innerHeight || document.documentElement.clientHeight;
}

// Get the current horizontal page scroll position
function findScrollX()
{
	return window.scrollX || document.documentElement.scrollLeft;
}

// Get the current vertical page scroll position
function findScrollY()
{
	return window.scrollY || document.documentElement.scrollTop;
}

function findPosX(obj)
{
	var curleft = 0;
	while(obj.offsetParent) {
		curleft += obj.offsetLeft;
		obj = obj.offsetParent;
	}
	return curleft;
}

function findPosY(obj)
{
	var curtop = 0;
	while(obj.offsetParent) {
		curtop += obj.offsetTop;
		obj = obj.offsetParent;
	}
	return curtop;
}

// Blur a particular element
function blurElement(e)
{
	if(e && e.focus && e.blur) {
		e.focus();
		e.blur();
	}
}

// Create a non-breaking space
function insertSpacer(place)
{
	var e = document.createTextNode(String.fromCharCode(160));
	if(place)
		place.appendChild(e);
	return e;
}

// Force the browser to do a document reflow when needed to workaround browser bugs
function forceReflow()
{
	if(config.browser.isGecko) {
		setStylesheet("body {top:0px;margin-top:0px;}","forceReflow");
		setTimeout(function() {setStylesheet("","forceReflow");},1);
	}
}

// Replace the current selection of a textarea or text input and scroll it into view
function replaceSelection(e,text)
{
	if(e.setSelectionRange) {
		var oldpos = e.selectionStart;
		var isRange = e.selectionEnd > e.selectionStart;
		e.value = e.value.substr(0,e.selectionStart) + text + e.value.substr(e.selectionEnd);
		e.setSelectionRange(isRange ? oldpos : oldpos + text.length,oldpos + text.length);
		var linecount = e.value.split("\n").length;
		var thisline = e.value.substr(0,e.selectionStart).split("\n").length-1;
		e.scrollTop = Math.floor((thisline - e.rows / 2) * e.scrollHeight / linecount);
	} else if(document.selection) {
		var range = document.selection.createRange();
		if(range.parentElement() == e) {
			var isCollapsed = range.text == "";
			range.text = text;
			if(!isCollapsed) {
				range.moveStart("character", -text.length);
				range.select();
			}
		}
	}
}

// Set the caret position in a text area
function setCaretPosition(e,pos)
{
	if(e.selectionStart || e.selectionStart == '0') {
		e.selectionStart = pos;
		e.selectionEnd = pos;
		e.focus();
	} else if(document.selection) {
		// IE support
		e.focus ();
		var sel = document.selection.createRange();
		sel.moveStart('character', -e.value.length);
		sel.moveStart('character',pos);
		sel.moveEnd('character',0);
		sel.select();
	}
}

// Returns the text of the given (text) node, possibly merging subsequent text nodes
function getNodeText(e)
{
	var t = "";
	while(e && e.nodeName == "#text") {
		t += e.nodeValue;
		e = e.nextSibling;
	}
	return t;
}

// Returns true if the element e has a given ancestor element
function isDescendant(e,ancestor)
{
	while(e) {
		if(e === ancestor)
			return true;
		e = e.parentNode;
	}
	return false;
}


// deprecate the following...

// Prevent an event from bubbling
function stopEvent(e)
{
	var ev = e || window.event;
	ev.cancelBubble = true;
	if(ev.stopPropagation) ev.stopPropagation();
	return false;
}

// Remove any event handlers or non-primitve custom attributes
function scrubNode(e)
{
	if(!config.browser.isIE)
		return;
	var att = e.attributes;
	if(att) {
		for(var t=0; t<att.length; t++) {
			var n = att[t].name;
			if(n !== "style" && (typeof e[n] === "function" || (typeof e[n] === "object" && e[n] != null))) {
				try {
					e[n] = null;
				} catch(ex) {
				}
			}
		}
	}
	var c = e.firstChild;
	while(c) {
		scrubNode(c);
		c = c.nextSibling;
	}
}

function addClass(e,className)
{
	jQuery(e).addClass(className);
}

function removeClass(e,className)
{
	jQuery(e).removeClass(className);
}

function hasClass(e,className)
{
	return jQuery(e).hasClass(className);
}

// Remove all children of a node
function removeChildren(e)
{
	jQuery(e).empty();
}

// Return the content of an element as plain text with no formatting
function getPlainText(e)
{
	return jQuery(e).text();
}

// Remove a node and all it's children
function removeNode(e)
{
	jQuery(e).remove();
}

//--
//-- LoaderBase and SaverBase
//--

function LoaderBase() {}

LoaderBase.prototype.loadTiddler = function(store,node,tiddlers)
{
	var title = this.getTitle(store,node);
	if(safeMode && store.isShadowTiddler(title))
		return;
	if(title) {
		var tiddler = store.createTiddler(title);
		this.internalizeTiddler(store,tiddler,title,node);
		tiddlers.push(tiddler);
	}
};

LoaderBase.prototype.loadTiddlers = function(store,nodes)
{
	var tiddlers = [];
	for(var t = 0; t < nodes.length; t++) {
		try {
			this.loadTiddler(store,nodes[t],tiddlers);
		} catch(ex) {
			showException(ex,config.messages.tiddlerLoadError.format([this.getTitle(store,nodes[t])]));
		}
	}
	return tiddlers;
};

function SaverBase() {}

SaverBase.prototype.externalize = function(store)
{
	var results = [];
	var tiddlers = store.getTiddlers("title");
	for(var t = 0; t < tiddlers.length; t++) {
		if(!tiddlers[t].doNotSave())
			results.push(this.externalizeTiddler(store, tiddlers[t]));
	}
	return results.join("\n");
};

//--
//-- TW21Loader (inherits from LoaderBase)
//--

function TW21Loader() {}

TW21Loader.prototype = new LoaderBase();

TW21Loader.prototype.getTitle = function(store,node)
{
	var title = null;
	if(node.getAttribute) {
		title = node.getAttribute("title");
		if(!title)
			title = node.getAttribute("tiddler");
	}
	if(!title && node.id) {
		var lenPrefix = store.idPrefix.length;
		if(node.id.substr(0,lenPrefix) == store.idPrefix)
			title = node.id.substr(lenPrefix);
	}
	return title;
};

TW21Loader.prototype.internalizeTiddler = function(store,tiddler,title,node)
{
	var e = node.firstChild;
	var text = null;
	if(node.getAttribute("tiddler")) {
		text = getNodeText(e).unescapeLineBreaks();
	} else {
		while(e.nodeName!="PRE" && e.nodeName!="pre") {
			e = e.nextSibling;
		}
		text = e.innerHTML.replace(/\r/mg,"").htmlDecode();
	}
	var creator = node.getAttribute("creator");
	var modifier = node.getAttribute("modifier");
	var c = node.getAttribute("created");
	var m = node.getAttribute("modified");
	var created = c ? Date.convertFromYYYYMMDDHHMMSS(c) : version.date;
	var modified = m ? Date.convertFromYYYYMMDDHHMMSS(m) : created;
	var tags = node.getAttribute("tags");
	var fields = {};
	var attrs = node.attributes;
	for(var i = attrs.length-1; i >= 0; i--) {
		var name = attrs[i].name;
		if(attrs[i].specified && !TiddlyWiki.isStandardField(name)) {
			fields[name] = attrs[i].value.unescapeLineBreaks();
		}
	}
	tiddler.assign(title,text,modifier,modified,tags,created,fields,creator);
	return tiddler;
};

//--
//-- TW21Saver (inherits from SaverBase)
//--

function TW21Saver() {}

TW21Saver.prototype = new SaverBase();

TW21Saver.prototype.externalizeTiddler = function(store,tiddler)
{
	try {
		var extendedAttributes = "";
		var usePre = config.options.chkUsePreForStorage;
		store.forEachField(tiddler,
			function(tiddler,fieldName,value) {
				// don't store stuff from the temp namespace
				if(typeof value != "string")
					value = "";
				if(!fieldName.match(/^temp\./))
					extendedAttributes += ' %0="%1"'.format([fieldName,value.escapeLineBreaks().htmlEncode()]);
			},true);
		var created = tiddler.created;
		var modified = tiddler.modified;
		var attributes = tiddler.creator ? ' creator="' + tiddler.creator.htmlEncode() + '"' : "";
		attributes += tiddler.modifier ? ' modifier="' + tiddler.modifier.htmlEncode() + '"' : "";
		attributes += (usePre && created == version.date) ? "" :' created="' + created.convertToYYYYMMDDHHMM() + '"';
		attributes += (usePre && modified == created) ? "" : ' modified="' + modified.convertToYYYYMMDDHHMM() +'"';
		var tags = tiddler.getTags();
		if(!usePre || tags)
			attributes += ' tags="' + tags.htmlEncode() + '"';
		return ('<div %0="%1"%2%3>%4</'+'div>').format([
				usePre ? "title" : "tiddler",
				tiddler.title.htmlEncode(),
				attributes,
				extendedAttributes,
				usePre ? "\n<pre>" + tiddler.text.htmlEncode() + "</pre>\n" : tiddler.text.escapeLineBreaks().htmlEncode()
			]);
	} catch (ex) {
		throw exceptionText(ex,config.messages.tiddlerSaveError.format([tiddler.title]));
	}
};

//]]>
</script>
<script id="jsdeprecatedArea" type="text/javascript">
//<![CDATA[
//--
//-- Deprecated Crypto functions and associated conversion routines.
//-- Use the jQuery.encoding functions directly instead.
//--

// Crypto 'namespace'
function Crypto() {}

// Convert a string to an array of big-endian 32-bit words
Crypto.strToBe32s = function(str)
{
	return jQuery.encoding.strToBe32s(str);
};

// Convert an array of big-endian 32-bit words to a string
Crypto.be32sToStr = function(be)
{
	return jQuery.encoding.be32sToStr(be);
};

// Convert an array of big-endian 32-bit words to a hex string
Crypto.be32sToHex = function(be)
{
	return jQuery.encoding.be32sToHex(be);
};

// Return, in hex, the SHA-1 hash of a string
Crypto.hexSha1Str = function(str)
{
	return jQuery.encoding.digests.hexSha1Str(str);
};

// Return the SHA-1 hash of a string
Crypto.sha1Str = function(str)
{
	return jQuery.encoding.digests.sha1Str(str);
};

// Calculate the SHA-1 hash of an array of blen bytes of big-endian 32-bit words
Crypto.sha1 = function(x,blen)
{
	return jQuery.encoding.digests.sha1(x,blen);
};

//--
//-- Deprecated DOM utilities
//--

// @Deprecated: Use jQuery.stylesheet instead
function setStylesheet(s,id,doc)
{
	jQuery.twStylesheet(s,{ id: id, doc: doc });
}

// @Deprecated: Use jQuery.stylesheet.remove instead
function removeStyleSheet(id)
{
	jQuery.twStylesheet.remove({ id: id });
}
//--
//-- Deprecated HTTP request code
//-- Use the jQuery ajax functions directly instead
//--

function loadRemoteFile(url,callback,params)
{
	return httpReq("GET",url,callback,params);
}

function doHttp(type,url,data,contentType,username,password,callback,params,headers,allowCache)
{
	return httpReq(type,url,callback,params,headers,data,contentType,username,password,allowCache);
}

function httpReq(type,url,callback,params,headers,data,contentType,username,password,allowCache)
{
	var options = {
		type:type,
		url:url,
		processData:false,
		data:data,
		cache:!!allowCache,
		beforeSend: function(xhr) {
			for(var i in headers)
				xhr.setRequestHeader(i,headers[i]);
			xhr.setRequestHeader("X-Requested-With", "TiddlyWiki " + formatVersion());
		}
	};

	if(callback) {
		options.complete = function(xhr,textStatus) {
			if(jQuery.httpSuccess(xhr))
				callback(true,params,xhr.responseText,url,xhr);
			else
				callback(false,params,null,url,xhr);
		};
	}
	if(contentType)
		options.contentType = contentType;
	if(username)
		options.username = username;
	if(password)
		options.password = password;
	if(window.Components && window.netscape && window.netscape.security && document.location.protocol.indexOf("http") == -1)
		window.netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
	return jQuery.ajax(options);
}

//--
//-- Deprecated String functions
//--

// @Deprecated: no direct replacement, since not used in core code
String.prototype.toJSONString = function()
{
	// Convert a string to it's JSON representation by encoding control characters, double quotes and backslash. See json.org
	var m = {
		'\b': '\\b',
		'\f': '\\f',
		'\n': '\\n',
		'\r': '\\r',
		'\t': '\\t',
		'"' : '\\"',
		'\\': '\\\\'
		};
	var replaceFn = function(a,b) {
		var c = m[b];
		if(c)
			return c;
		c = b.charCodeAt();
		return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
		};
	if(/["\\\x00-\x1f]/.test(this))
		return '"' + this.replace(/([\x00-\x1f\\"])/g,replaceFn) + '"';
	return '"' + this + '"';
};

//--
//-- Deprecated Tiddler code
//--

// @Deprecated: Use tiddlerToRssItem(tiddler,uri) instead
Tiddler.prototype.toRssItem = function(uri)
{
	return tiddlerToRssItem(this,uri);
};

// @Deprecated: Use "<item>\n" + tiddlerToRssItem(tiddler,uri)  + "\n</item>" instead
Tiddler.prototype.saveToRss = function(uri)
{
	return "<item>\n" + tiddlerToRssItem(this,uri) + "\n</item>";
};

// @Deprecated: Use jQuery.encoding.digests.hexSha1Str instead
Tiddler.prototype.generateFingerprint = function()
{
	return "0x" + Crypto.hexSha1Str(this.text);
};

//]]>
</script>
<script id="jslibArea" type="text/javascript">
//<![CDATA[
/*!
 * jQuery JavaScript Library v1.4.3
 * http://jquery.com/
 *
 * Copyright 2010, John Resig
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * Includes Sizzle.js
 * http://sizzlejs.com/
 * Copyright 2010, The Dojo Foundation
 * Released under the MIT, BSD, and GPL Licenses.
 *
 * Date: Thu Oct 14 23:10:06 2010 -0400
 */
(function(E,A){function U(){return false}function ba(){return true}function ja(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function Ga(a){var b,d,e=[],f=[],h,k,l,n,s,v,B,D;k=c.data(this,this.nodeType?"events":"__events__");if(typeof k==="function")k=k.events;if(!(a.liveFired===this||!k||!k.live||a.button&&a.type==="click")){if(a.namespace)D=RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)");a.liveFired=this;var H=k.live.slice(0);for(n=0;n<H.length;n++){k=H[n];k.origType.replace(X,
"")===a.type?f.push(k.selector):H.splice(n--,1)}f=c(a.target).closest(f,a.currentTarget);s=0;for(v=f.length;s<v;s++){B=f[s];for(n=0;n<H.length;n++){k=H[n];if(B.selector===k.selector&&(!D||D.test(k.namespace))){l=B.elem;h=null;if(k.preType==="mouseenter"||k.preType==="mouseleave"){a.type=k.preType;h=c(a.relatedTarget).closest(k.selector)[0]}if(!h||h!==l)e.push({elem:l,handleObj:k,level:B.level})}}}s=0;for(v=e.length;s<v;s++){f=e[s];if(d&&f.level>d)break;a.currentTarget=f.elem;a.data=f.handleObj.data;
a.handleObj=f.handleObj;D=f.handleObj.origHandler.apply(f.elem,arguments);if(D===false||a.isPropagationStopped()){d=f.level;if(D===false)b=false}}return b}}function Y(a,b){return(a&&a!=="*"?a+".":"")+b.replace(Ha,"`").replace(Ia,"&")}function ka(a,b,d){if(c.isFunction(b))return c.grep(a,function(f,h){return!!b.call(f,h,f)===d});else if(b.nodeType)return c.grep(a,function(f){return f===b===d});else if(typeof b==="string"){var e=c.grep(a,function(f){return f.nodeType===1});if(Ja.test(b))return c.filter(b,
e,!d);else b=c.filter(b,e)}return c.grep(a,function(f){return c.inArray(f,b)>=0===d})}function la(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var e=c.data(a[d++]),f=c.data(this,e);if(e=e&&e.events){delete f.handle;f.events={};for(var h in e)for(var k in e[h])c.event.add(this,h,e[h][k],e[h][k].data)}}})}function Ka(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}
function ma(a,b,d){var e=b==="width"?a.offsetWidth:a.offsetHeight;if(d==="border")return e;c.each(b==="width"?La:Ma,function(){d||(e-=parseFloat(c.css(a,"padding"+this))||0);if(d==="margin")e+=parseFloat(c.css(a,"margin"+this))||0;else e-=parseFloat(c.css(a,"border"+this+"Width"))||0});return e}function ca(a,b,d,e){if(c.isArray(b)&&b.length)c.each(b,function(f,h){d||Na.test(a)?e(a,h):ca(a+"["+(typeof h==="object"||c.isArray(h)?f:"")+"]",h,d,e)});else if(!d&&b!=null&&typeof b==="object")c.isEmptyObject(b)?
e(a,""):c.each(b,function(f,h){ca(a+"["+f+"]",h,d,e)});else e(a,b)}function S(a,b){var d={};c.each(na.concat.apply([],na.slice(0,b)),function(){d[this]=a});return d}function oa(a){if(!da[a]){var b=c("<"+a+">").appendTo("body"),d=b.css("display");b.remove();if(d==="none"||d==="")d="block";da[a]=d}return da[a]}function ea(a){return c.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var u=E.document,c=function(){function a(){if(!b.isReady){try{u.documentElement.doScroll("left")}catch(i){setTimeout(a,
1);return}b.ready()}}var b=function(i,r){return new b.fn.init(i,r)},d=E.jQuery,e=E.$,f,h=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/,k=/\S/,l=/^\s+/,n=/\s+$/,s=/\W/,v=/\d/,B=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,D=/^[\],:{}\s]*$/,H=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,w=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,G=/(?:^|:|,)(?:\s*\[)+/g,M=/(webkit)[ \/]([\w.]+)/,g=/(opera)(?:.*version)?[ \/]([\w.]+)/,j=/(msie) ([\w.]+)/,o=/(mozilla)(?:.*? rv:([\w.]+))?/,m=navigator.userAgent,p=false,
q=[],t,x=Object.prototype.toString,C=Object.prototype.hasOwnProperty,P=Array.prototype.push,N=Array.prototype.slice,R=String.prototype.trim,Q=Array.prototype.indexOf,L={};b.fn=b.prototype={init:function(i,r){var y,z,F;if(!i)return this;if(i.nodeType){this.context=this[0]=i;this.length=1;return this}if(i==="body"&&!r&&u.body){this.context=u;this[0]=u.body;this.selector="body";this.length=1;return this}if(typeof i==="string")if((y=h.exec(i))&&(y[1]||!r))if(y[1]){F=r?r.ownerDocument||r:u;if(z=B.exec(i))if(b.isPlainObject(r)){i=
[u.createElement(z[1])];b.fn.attr.call(i,r,true)}else i=[F.createElement(z[1])];else{z=b.buildFragment([y[1]],[F]);i=(z.cacheable?z.fragment.cloneNode(true):z.fragment).childNodes}return b.merge(this,i)}else{if((z=u.getElementById(y[2]))&&z.parentNode){if(z.id!==y[2])return f.find(i);this.length=1;this[0]=z}this.context=u;this.selector=i;return this}else if(!r&&!s.test(i)){this.selector=i;this.context=u;i=u.getElementsByTagName(i);return b.merge(this,i)}else return!r||r.jquery?(r||f).find(i):b(r).find(i);
else if(b.isFunction(i))return f.ready(i);if(i.selector!==A){this.selector=i.selector;this.context=i.context}return b.makeArray(i,this)},selector:"",jquery:"1.4.3",length:0,size:function(){return this.length},toArray:function(){return N.call(this,0)},get:function(i){return i==null?this.toArray():i<0?this.slice(i)[0]:this[i]},pushStack:function(i,r,y){var z=b();b.isArray(i)?P.apply(z,i):b.merge(z,i);z.prevObject=this;z.context=this.context;if(r==="find")z.selector=this.selector+(this.selector?" ":
"")+y;else if(r)z.selector=this.selector+"."+r+"("+y+")";return z},each:function(i,r){return b.each(this,i,r)},ready:function(i){b.bindReady();if(b.isReady)i.call(u,b);else q&&q.push(i);return this},eq:function(i){return i===-1?this.slice(i):this.slice(i,+i+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(N.apply(this,arguments),"slice",N.call(arguments).join(","))},map:function(i){return this.pushStack(b.map(this,function(r,y){return i.call(r,
y,r)}))},end:function(){return this.prevObject||b(null)},push:P,sort:[].sort,splice:[].splice};b.fn.init.prototype=b.fn;b.extend=b.fn.extend=function(){var i=arguments[0]||{},r=1,y=arguments.length,z=false,F,I,K,J,fa;if(typeof i==="boolean"){z=i;i=arguments[1]||{};r=2}if(typeof i!=="object"&&!b.isFunction(i))i={};if(y===r){i=this;--r}for(;r<y;r++)if((F=arguments[r])!=null)for(I in F){K=i[I];J=F[I];if(i!==J)if(z&&J&&(b.isPlainObject(J)||(fa=b.isArray(J)))){if(fa){fa=false;clone=K&&b.isArray(K)?K:[]}else clone=
K&&b.isPlainObject(K)?K:{};i[I]=b.extend(z,clone,J)}else if(J!==A)i[I]=J}return i};b.extend({noConflict:function(i){E.$=e;if(i)E.jQuery=d;return b},isReady:false,readyWait:1,ready:function(i){i===true&&b.readyWait--;if(!b.readyWait||i!==true&&!b.isReady){if(!u.body)return setTimeout(b.ready,1);b.isReady=true;if(!(i!==true&&--b.readyWait>0)){if(q){for(var r=0;i=q[r++];)i.call(u,b);q=null}b.fn.triggerHandler&&b(u).triggerHandler("ready")}}},bindReady:function(){if(!p){p=true;if(u.readyState==="complete")return setTimeout(b.ready,
1);if(u.addEventListener){u.addEventListener("DOMContentLoaded",t,false);E.addEventListener("load",b.ready,false)}else if(u.attachEvent){u.attachEvent("onreadystatechange",t);E.attachEvent("onload",b.ready);var i=false;try{i=E.frameElement==null}catch(r){}u.documentElement.doScroll&&i&&a()}}},isFunction:function(i){return b.type(i)==="function"},isArray:Array.isArray||function(i){return b.type(i)==="array"},isWindow:function(i){return i&&typeof i==="object"&&"setInterval"in i},isNaN:function(i){return i==
null||!v.test(i)||isNaN(i)},type:function(i){return i==null?String(i):L[x.call(i)]||"object"},isPlainObject:function(i){if(!i||b.type(i)!=="object"||i.nodeType||b.isWindow(i))return false;if(i.constructor&&!C.call(i,"constructor")&&!C.call(i.constructor.prototype,"isPrototypeOf"))return false;for(var r in i);return r===A||C.call(i,r)},isEmptyObject:function(i){for(var r in i)return false;return true},error:function(i){throw i;},parseJSON:function(i){if(typeof i!=="string"||!i)return null;i=b.trim(i);
if(D.test(i.replace(H,"@").replace(w,"]").replace(G,"")))return E.JSON&&E.JSON.parse?E.JSON.parse(i):(new Function("return "+i))();else b.error("Invalid JSON: "+i)},noop:function(){},globalEval:function(i){if(i&&k.test(i)){var r=u.getElementsByTagName("head")[0]||u.documentElement,y=u.createElement("script");y.type="text/javascript";if(b.support.scriptEval)y.appendChild(u.createTextNode(i));else y.text=i;r.insertBefore(y,r.firstChild);r.removeChild(y)}},nodeName:function(i,r){return i.nodeName&&i.nodeName.toUpperCase()===
r.toUpperCase()},each:function(i,r,y){var z,F=0,I=i.length,K=I===A||b.isFunction(i);if(y)if(K)for(z in i){if(r.apply(i[z],y)===false)break}else for(;F<I;){if(r.apply(i[F++],y)===false)break}else if(K)for(z in i){if(r.call(i[z],z,i[z])===false)break}else for(y=i[0];F<I&&r.call(y,F,y)!==false;y=i[++F]);return i},trim:R?function(i){return i==null?"":R.call(i)}:function(i){return i==null?"":i.toString().replace(l,"").replace(n,"")},makeArray:function(i,r){var y=r||[];if(i!=null){var z=b.type(i);i.length==
null||z==="string"||z==="function"||z==="regexp"||b.isWindow(i)?P.call(y,i):b.merge(y,i)}return y},inArray:function(i,r){if(r.indexOf)return r.indexOf(i);for(var y=0,z=r.length;y<z;y++)if(r[y]===i)return y;return-1},merge:function(i,r){var y=i.length,z=0;if(typeof r.length==="number")for(var F=r.length;z<F;z++)i[y++]=r[z];else for(;r[z]!==A;)i[y++]=r[z++];i.length=y;return i},grep:function(i,r,y){var z=[],F;y=!!y;for(var I=0,K=i.length;I<K;I++){F=!!r(i[I],I);y!==F&&z.push(i[I])}return z},map:function(i,
r,y){for(var z=[],F,I=0,K=i.length;I<K;I++){F=r(i[I],I,y);if(F!=null)z[z.length]=F}return z.concat.apply([],z)},guid:1,proxy:function(i,r,y){if(arguments.length===2)if(typeof r==="string"){y=i;i=y[r];r=A}else if(r&&!b.isFunction(r)){y=r;r=A}if(!r&&i)r=function(){return i.apply(y||this,arguments)};if(i)r.guid=i.guid=i.guid||r.guid||b.guid++;return r},access:function(i,r,y,z,F,I){var K=i.length;if(typeof r==="object"){for(var J in r)b.access(i,J,r[J],z,F,y);return i}if(y!==A){z=!I&&z&&b.isFunction(y);
for(J=0;J<K;J++)F(i[J],r,z?y.call(i[J],J,F(i[J],r)):y,I);return i}return K?F(i[0],r):A},now:function(){return(new Date).getTime()},uaMatch:function(i){i=i.toLowerCase();i=M.exec(i)||g.exec(i)||j.exec(i)||i.indexOf("compatible")<0&&o.exec(i)||[];return{browser:i[1]||"",version:i[2]||"0"}},browser:{}});b.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(i,r){L["[object "+r+"]"]=r.toLowerCase()});m=b.uaMatch(m);if(m.browser){b.browser[m.browser]=true;b.browser.version=
m.version}if(b.browser.webkit)b.browser.safari=true;if(Q)b.inArray=function(i,r){return Q.call(r,i)};if(!/\s/.test("\u00a0")){l=/^[\s\xA0]+/;n=/[\s\xA0]+$/}f=b(u);if(u.addEventListener)t=function(){u.removeEventListener("DOMContentLoaded",t,false);b.ready()};else if(u.attachEvent)t=function(){if(u.readyState==="complete"){u.detachEvent("onreadystatechange",t);b.ready()}};return E.jQuery=E.$=b}();(function(){c.support={};var a=u.documentElement,b=u.createElement("script"),d=u.createElement("div"),
e="script"+c.now();d.style.display="none";d.innerHTML="   <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";var f=d.getElementsByTagName("*"),h=d.getElementsByTagName("a")[0],k=u.createElement("select"),l=k.appendChild(u.createElement("option"));if(!(!f||!f.length||!h)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(h.getAttribute("style")),
hrefNormalized:h.getAttribute("href")==="/a",opacity:/^0.55$/.test(h.style.opacity),cssFloat:!!h.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:l.selected,optDisabled:false,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null,inlineBlockNeedsLayout:false,shrinkWrapBlocks:false,reliableHiddenOffsets:true};k.disabled=true;c.support.optDisabled=!l.disabled;b.type="text/javascript";try{b.appendChild(u.createTextNode("window."+e+"=1;"))}catch(n){}a.insertBefore(b,
a.firstChild);if(E[e]){c.support.scriptEval=true;delete E[e]}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function s(){c.support.noCloneEvent=false;d.detachEvent("onclick",s)});d.cloneNode(true).fireEvent("onclick")}d=u.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=u.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var s=u.createElement("div");
s.style.width=s.style.paddingLeft="1px";u.body.appendChild(s);c.boxModel=c.support.boxModel=s.offsetWidth===2;if("zoom"in s.style){s.style.display="inline";s.style.zoom=1;c.support.inlineBlockNeedsLayout=s.offsetWidth===2;s.style.display="";s.innerHTML="<div style='width:4px;'></div>";c.support.shrinkWrapBlocks=s.offsetWidth!==2}s.innerHTML="<table><tr><td style='padding:0;display:none'></td><td>t</td></tr></table>";var v=s.getElementsByTagName("td");c.support.reliableHiddenOffsets=v[0].offsetHeight===
0;v[0].style.display="";v[1].style.display="none";c.support.reliableHiddenOffsets=c.support.reliableHiddenOffsets&&v[0].offsetHeight===0;s.innerHTML="";u.body.removeChild(s).style.display="none"});a=function(s){var v=u.createElement("div");s="on"+s;var B=s in v;if(!B){v.setAttribute(s,"return;");B=typeof v[s]==="function"}return B};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=f=h=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",
cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var pa={},Oa=/^(?:\{.*\}|\[.*\])$/;c.extend({cache:{},uuid:0,expando:"jQuery"+c.now(),noData:{embed:true,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:true},data:function(a,b,d){if(c.acceptData(a)){a=a==E?pa:a;var e=a.nodeType,f=e?a[c.expando]:null,h=c.cache;if(!(e&&!f&&typeof b==="string"&&d===A)){if(e)f||(a[c.expando]=f=++c.uuid);else h=a;if(typeof b==="object")if(e)h[f]=
c.extend(h[f],b);else c.extend(h,b);else if(e&&!h[f])h[f]={};a=e?h[f]:h;if(d!==A)a[b]=d;return typeof b==="string"?a[b]:a}}},removeData:function(a,b){if(c.acceptData(a)){a=a==E?pa:a;var d=a.nodeType,e=d?a[c.expando]:a,f=c.cache,h=d?f[e]:e;if(b){if(h){delete h[b];d&&c.isEmptyObject(h)&&c.removeData(a)}}else if(d&&c.support.deleteExpando)delete a[c.expando];else if(a.removeAttribute)a.removeAttribute(c.expando);else if(d)delete f[e];else for(var k in a)delete a[k]}},acceptData:function(a){if(a.nodeName){var b=
c.noData[a.nodeName.toLowerCase()];if(b)return!(b===true||a.getAttribute("classid")!==b)}return true}});c.fn.extend({data:function(a,b){if(typeof a==="undefined")return this.length?c.data(this[0]):null;else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===A){var e=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(e===A&&this.length){e=c.data(this[0],a);if(e===A&&this[0].nodeType===1){e=this[0].getAttribute("data-"+a);if(typeof e===
"string")try{e=e==="true"?true:e==="false"?false:e==="null"?null:!c.isNaN(e)?parseFloat(e):Oa.test(e)?c.parseJSON(e):e}catch(f){}else e=A}}return e===A&&d[1]?this.data(d[0]):e}else return this.each(function(){var h=c(this),k=[d[0],b];h.triggerHandler("setData"+d[1]+"!",k);c.data(this,a,b);h.triggerHandler("changeData"+d[1]+"!",k)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var e=c.data(a,b);if(!d)return e||
[];if(!e||c.isArray(d))e=c.data(a,b,c.makeArray(d));else e.push(d);return e}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),e=d.shift();if(e==="inprogress")e=d.shift();if(e){b==="fx"&&d.unshift("inprogress");e.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===A)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,
a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var qa=/[\n\t]/g,ga=/\s+/,Pa=/\r/g,Qa=/^(?:href|src|style)$/,Ra=/^(?:button|input)$/i,Sa=/^(?:button|input|object|select|textarea)$/i,Ta=/^a(?:rea)?$/i,ra=/^(?:radio|checkbox)$/i;c.fn.extend({attr:function(a,b){return c.access(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,
a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(s){var v=c(this);v.addClass(a.call(this,s,v.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ga),d=0,e=this.length;d<e;d++){var f=this[d];if(f.nodeType===1)if(f.className){for(var h=" "+f.className+" ",k=f.className,l=0,n=b.length;l<n;l++)if(h.indexOf(" "+b[l]+" ")<0)k+=" "+b[l];f.className=c.trim(k)}else f.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(n){var s=
c(this);s.removeClass(a.call(this,n,s.attr("class")))});if(a&&typeof a==="string"||a===A)for(var b=(a||"").split(ga),d=0,e=this.length;d<e;d++){var f=this[d];if(f.nodeType===1&&f.className)if(a){for(var h=(" "+f.className+" ").replace(qa," "),k=0,l=b.length;k<l;k++)h=h.replace(" "+b[k]+" "," ");f.className=c.trim(h)}else f.className=""}return this},toggleClass:function(a,b){var d=typeof a,e=typeof b==="boolean";if(c.isFunction(a))return this.each(function(f){var h=c(this);h.toggleClass(a.call(this,
f,h.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var f,h=0,k=c(this),l=b,n=a.split(ga);f=n[h++];){l=e?l:!k.hasClass(f);k[l?"addClass":"removeClass"](f)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(qa," ").indexOf(a)>-1)return true;return false},
val:function(a){if(!arguments.length){var b=this[0];if(b){if(c.nodeName(b,"option")){var d=b.attributes.value;return!d||d.specified?b.value:b.text}if(c.nodeName(b,"select")){var e=b.selectedIndex;d=[];var f=b.options;b=b.type==="select-one";if(e<0)return null;var h=b?e:0;for(e=b?e+1:f.length;h<e;h++){var k=f[h];if(k.selected&&(c.support.optDisabled?!k.disabled:k.getAttribute("disabled")===null)&&(!k.parentNode.disabled||!c.nodeName(k.parentNode,"optgroup"))){a=c(k).val();if(b)return a;d.push(a)}}return d}if(ra.test(b.type)&&
!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Pa,"")}return A}var l=c.isFunction(a);return this.each(function(n){var s=c(this),v=a;if(this.nodeType===1){if(l)v=a.call(this,n,s.val());if(v==null)v="";else if(typeof v==="number")v+="";else if(c.isArray(v))v=c.map(v,function(D){return D==null?"":D+""});if(c.isArray(v)&&ra.test(this.type))this.checked=c.inArray(s.val(),v)>=0;else if(c.nodeName(this,"select")){var B=c.makeArray(v);c("option",this).each(function(){this.selected=
c.inArray(c(this).val(),B)>=0});if(!B.length)this.selectedIndex=-1}else this.value=v}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,e){if(!a||a.nodeType===3||a.nodeType===8)return A;if(e&&b in c.attrFn)return c(a)[b](d);e=a.nodeType!==1||!c.isXMLDoc(a);var f=d!==A;b=e&&c.props[b]||b;if(a.nodeType===1){var h=Qa.test(b);if((b in a||a[b]!==A)&&e&&!h){if(f){b==="type"&&Ra.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");
if(d===null)a.nodeType===1&&a.removeAttribute(b);else a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:Sa.test(a.nodeName)||Ta.test(a.nodeName)&&a.href?0:A;return a[b]}if(!c.support.style&&e&&b==="style"){if(f)a.style.cssText=""+d;return a.style.cssText}f&&a.setAttribute(b,""+d);if(!a.attributes[b]&&a.hasAttribute&&!a.hasAttribute(b))return A;a=!c.support.hrefNormalized&&e&&
h?a.getAttribute(b,2):a.getAttribute(b);return a===null?A:a}}});var X=/\.(.*)$/,ha=/^(?:textarea|input|select)$/i,Ha=/\./g,Ia=/ /g,Ua=/[^\w\s.|`]/g,Va=function(a){return a.replace(Ua,"\\$&")},sa={focusin:0,focusout:0};c.event={add:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(c.isWindow(a)&&a!==E&&!a.frameElement)a=E;if(d===false)d=U;var f,h;if(d.handler){f=d;d=f.handler}if(!d.guid)d.guid=c.guid++;if(h=c.data(a)){var k=a.nodeType?"events":"__events__",l=h[k],n=h.handle;if(typeof l===
"function"){n=l.handle;l=l.events}else if(!l){a.nodeType||(h[k]=h=function(){});h.events=l={}}if(!n)h.handle=n=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(n.elem,arguments):A};n.elem=a;b=b.split(" ");for(var s=0,v;k=b[s++];){h=f?c.extend({},f):{handler:d,data:e};if(k.indexOf(".")>-1){v=k.split(".");k=v.shift();h.namespace=v.slice(0).sort().join(".")}else{v=[];h.namespace=""}h.type=k;if(!h.guid)h.guid=d.guid;var B=l[k],D=c.event.special[k]||{};if(!B){B=l[k]=[];
if(!D.setup||D.setup.call(a,e,v,n)===false)if(a.addEventListener)a.addEventListener(k,n,false);else a.attachEvent&&a.attachEvent("on"+k,n)}if(D.add){D.add.call(a,h);if(!h.handler.guid)h.handler.guid=d.guid}B.push(h);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(d===false)d=U;var f,h,k=0,l,n,s,v,B,D,H=a.nodeType?"events":"__events__",w=c.data(a),G=w&&w[H];if(w&&G){if(typeof G==="function"){w=G;G=G.events}if(b&&b.type){d=b.handler;b=b.type}if(!b||
typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(f in G)c.event.remove(a,f+b)}else{for(b=b.split(" ");f=b[k++];){v=f;l=f.indexOf(".")<0;n=[];if(!l){n=f.split(".");f=n.shift();s=RegExp("(^|\\.)"+c.map(n.slice(0).sort(),Va).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(B=G[f])if(d){v=c.event.special[f]||{};for(h=e||0;h<B.length;h++){D=B[h];if(d.guid===D.guid){if(l||s.test(D.namespace)){e==null&&B.splice(h--,1);v.remove&&v.remove.call(a,D)}if(e!=null)break}}if(B.length===0||e!=null&&B.length===1){if(!v.teardown||
v.teardown.call(a,n)===false)c.removeEvent(a,f,w.handle);delete G[f]}}else for(h=0;h<B.length;h++){D=B[h];if(l||s.test(D.namespace)){c.event.remove(a,v,D.handler,h);B.splice(h--,1)}}}if(c.isEmptyObject(G)){if(b=w.handle)b.elem=null;delete w.events;delete w.handle;if(typeof w==="function")c.removeData(a,H);else c.isEmptyObject(w)&&c.removeData(a)}}}}},trigger:function(a,b,d,e){var f=a.type||a;if(!e){a=typeof a==="object"?a[c.expando]?a:c.extend(c.Event(f),a):c.Event(f);if(f.indexOf("!")>=0){a.type=
f=f.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[f]&&c.each(c.cache,function(){this.events&&this.events[f]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return A;a.result=A;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(e=d.nodeType?c.data(d,"handle"):(c.data(d,"__events__")||{}).handle)&&e.apply(d,b);e=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+f]&&d["on"+f].apply(d,b)===
false){a.result=false;a.preventDefault()}}catch(h){}if(!a.isPropagationStopped()&&e)c.event.trigger(a,b,e,true);else if(!a.isDefaultPrevented()){e=a.target;var k,l=f.replace(X,""),n=c.nodeName(e,"a")&&l==="click",s=c.event.special[l]||{};if((!s._default||s._default.call(d,a)===false)&&!n&&!(e&&e.nodeName&&c.noData[e.nodeName.toLowerCase()])){try{if(e[l]){if(k=e["on"+l])e["on"+l]=null;c.event.triggered=true;e[l]()}}catch(v){}if(k)e["on"+l]=k;c.event.triggered=false}}},handle:function(a){var b,d,e;
d=[];var f,h=c.makeArray(arguments);a=h[0]=c.event.fix(a||E.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;if(!b){e=a.type.split(".");a.type=e.shift();d=e.slice(0).sort();e=RegExp("(^|\\.)"+d.join("\\.(?:.*\\.)?")+"(\\.|$)")}a.namespace=a.namespace||d.join(".");f=c.data(this,this.nodeType?"events":"__events__");if(typeof f==="function")f=f.events;d=(f||{})[a.type];if(f&&d){d=d.slice(0);f=0;for(var k=d.length;f<k;f++){var l=d[f];if(b||e.test(l.namespace)){a.handler=l.handler;a.data=
l.data;a.handleObj=l;l=l.handler.apply(this,h);if(l!==A){a.result=l;if(l===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
fix:function(a){if(a[c.expando])return a;var b=a;a=c.Event(b);for(var d=this.props.length,e;d;){e=this.props[--d];a[e]=b[e]}if(!a.target)a.target=a.srcElement||u;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=u.documentElement;d=u.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(a.which==null&&(a.charCode!=null||a.keyCode!=null))a.which=a.charCode!=null?a.charCode:a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==A)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,Y(a.origType,a.selector),c.extend({},a,{handler:Ga,guid:a.handler.guid}))},remove:function(a){c.event.remove(this,
Y(a.origType,a.selector),a)}},beforeunload:{setup:function(a,b,d){if(c.isWindow(this))this.onbeforeunload=d},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};c.removeEvent=u.removeEventListener?function(a,b,d){a.removeEventListener&&a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent&&a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=a;this.type=a.type}else this.type=a;this.timeStamp=
c.now();this[c.expando]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=ba;var a=this.originalEvent;if(a)if(a.preventDefault)a.preventDefault();else a.returnValue=false},stopPropagation:function(){this.isPropagationStopped=ba;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=ba;this.stopPropagation()},isDefaultPrevented:U,isPropagationStopped:U,isImmediatePropagationStopped:U};
var ta=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},ua=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?ua:ta,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?ua:ta)}}});if(!c.support.submitBubbles)c.event.special.submit={setup:function(){if(this.nodeName.toLowerCase()!==
"form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length){a.liveFired=A;return ja("submit",this,arguments)}});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13){a.liveFired=A;return ja("submit",this,arguments)}})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};if(!c.support.changeBubbles){var V,
va=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(e){return e.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},Z=function(a,b){var d=a.target,e,f;if(!(!ha.test(d.nodeName)||d.readOnly)){e=c.data(d,"_change_data");f=va(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",f);if(!(e===A||f===e))if(e!=null||f){a.type="change";a.liveFired=
A;return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:Z,beforedeactivate:Z,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return Z.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return Z.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,"_change_data",va(a))}},setup:function(){if(this.type===
"file")return false;for(var a in V)c.event.add(this,a+".specialChange",V[a]);return ha.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return ha.test(this.nodeName)}};V=c.event.special.change.filters;V.focus=V.beforeactivate}u.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(e){e=c.event.fix(e);e.type=b;return c.event.trigger(e,null,e.target)}c.event.special[b]={setup:function(){sa[b]++===0&&u.addEventListener(a,d,true)},teardown:function(){--sa[b]===
0&&u.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,e,f){if(typeof d==="object"){for(var h in d)this[b](h,e,d[h],f);return this}if(c.isFunction(e)||e===false){f=e;e=A}var k=b==="one"?c.proxy(f,function(n){c(this).unbind(n,k);return f.apply(this,arguments)}):f;if(d==="unload"&&b!=="one")this.one(d,e,f);else{h=0;for(var l=this.length;h<l;h++)c.event.add(this[h],d,k,e)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&!a.preventDefault)for(var d in a)this.unbind(d,
a[d]);else{d=0;for(var e=this.length;d<e;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,e){return this.live(b,d,e,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){var d=c.Event(a);d.preventDefault();d.stopPropagation();c.event.trigger(d,b,this[0]);return d.result}},toggle:function(a){for(var b=arguments,d=
1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(e){var f=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,f+1);e.preventDefault();return b[f].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var wa={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,e,f,h){var k,l=0,n,s,v=h||this.selector;h=h?this:c(this.context);if(typeof d===
"object"&&!d.preventDefault){for(k in d)h[b](k,e,d[k],v);return this}if(c.isFunction(e)){f=e;e=A}for(d=(d||"").split(" ");(k=d[l++])!=null;){n=X.exec(k);s="";if(n){s=n[0];k=k.replace(X,"")}if(k==="hover")d.push("mouseenter"+s,"mouseleave"+s);else{n=k;if(k==="focus"||k==="blur"){d.push(wa[k]+s);k+=s}else k=(wa[k]||k)+s;if(b==="live"){s=0;for(var B=h.length;s<B;s++)c.event.add(h[s],"live."+Y(k,v),{data:e,selector:v,handler:f,origType:k,origHandler:f,preType:n})}else h.unbind("live."+Y(k,v),f)}}return this}});
c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),function(a,b){c.fn[b]=function(d,e){if(e==null){e=d;d=null}return arguments.length>0?this.bind(b,d,e):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});E.attachEvent&&!E.addEventListener&&c(E).bind("unload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});
(function(){function a(g,j,o,m,p,q){p=0;for(var t=m.length;p<t;p++){var x=m[p];if(x){x=x[g];for(var C=false;x;){if(x.sizcache===o){C=m[x.sizset];break}if(x.nodeType===1&&!q){x.sizcache=o;x.sizset=p}if(x.nodeName.toLowerCase()===j){C=x;break}x=x[g]}m[p]=C}}}function b(g,j,o,m,p,q){p=0;for(var t=m.length;p<t;p++){var x=m[p];if(x){x=x[g];for(var C=false;x;){if(x.sizcache===o){C=m[x.sizset];break}if(x.nodeType===1){if(!q){x.sizcache=o;x.sizset=p}if(typeof j!=="string"){if(x===j){C=true;break}}else if(l.filter(j,
[x]).length>0){C=x;break}}x=x[g]}m[p]=C}}}var d=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,f=Object.prototype.toString,h=false,k=true;[0,0].sort(function(){k=false;return 0});var l=function(g,j,o,m){o=o||[];var p=j=j||u;if(j.nodeType!==1&&j.nodeType!==9)return[];if(!g||typeof g!=="string")return o;var q=[],t,x,C,P,N=true,R=l.isXML(j),Q=g,L;do{d.exec("");if(t=d.exec(Q)){Q=t[3];q.push(t[1]);if(t[2]){P=t[3];
break}}}while(t);if(q.length>1&&s.exec(g))if(q.length===2&&n.relative[q[0]])x=M(q[0]+q[1],j);else for(x=n.relative[q[0]]?[j]:l(q.shift(),j);q.length;){g=q.shift();if(n.relative[g])g+=q.shift();x=M(g,x)}else{if(!m&&q.length>1&&j.nodeType===9&&!R&&n.match.ID.test(q[0])&&!n.match.ID.test(q[q.length-1])){t=l.find(q.shift(),j,R);j=t.expr?l.filter(t.expr,t.set)[0]:t.set[0]}if(j){t=m?{expr:q.pop(),set:D(m)}:l.find(q.pop(),q.length===1&&(q[0]==="~"||q[0]==="+")&&j.parentNode?j.parentNode:j,R);x=t.expr?l.filter(t.expr,
t.set):t.set;if(q.length>0)C=D(x);else N=false;for(;q.length;){t=L=q.pop();if(n.relative[L])t=q.pop();else L="";if(t==null)t=j;n.relative[L](C,t,R)}}else C=[]}C||(C=x);C||l.error(L||g);if(f.call(C)==="[object Array]")if(N)if(j&&j.nodeType===1)for(g=0;C[g]!=null;g++){if(C[g]&&(C[g]===true||C[g].nodeType===1&&l.contains(j,C[g])))o.push(x[g])}else for(g=0;C[g]!=null;g++)C[g]&&C[g].nodeType===1&&o.push(x[g]);else o.push.apply(o,C);else D(C,o);if(P){l(P,p,o,m);l.uniqueSort(o)}return o};l.uniqueSort=function(g){if(w){h=
k;g.sort(w);if(h)for(var j=1;j<g.length;j++)g[j]===g[j-1]&&g.splice(j--,1)}return g};l.matches=function(g,j){return l(g,null,null,j)};l.matchesSelector=function(g,j){return l(j,null,null,[g]).length>0};l.find=function(g,j,o){var m;if(!g)return[];for(var p=0,q=n.order.length;p<q;p++){var t=n.order[p],x;if(x=n.leftMatch[t].exec(g)){var C=x[1];x.splice(1,1);if(C.substr(C.length-1)!=="\\"){x[1]=(x[1]||"").replace(/\\/g,"");m=n.find[t](x,j,o);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=j.getElementsByTagName("*"));
return{set:m,expr:g}};l.filter=function(g,j,o,m){for(var p=g,q=[],t=j,x,C,P=j&&j[0]&&l.isXML(j[0]);g&&j.length;){for(var N in n.filter)if((x=n.leftMatch[N].exec(g))!=null&&x[2]){var R=n.filter[N],Q,L;L=x[1];C=false;x.splice(1,1);if(L.substr(L.length-1)!=="\\"){if(t===q)q=[];if(n.preFilter[N])if(x=n.preFilter[N](x,t,o,q,m,P)){if(x===true)continue}else C=Q=true;if(x)for(var i=0;(L=t[i])!=null;i++)if(L){Q=R(L,x,i,t);var r=m^!!Q;if(o&&Q!=null)if(r)C=true;else t[i]=false;else if(r){q.push(L);C=true}}if(Q!==
A){o||(t=q);g=g.replace(n.match[N],"");if(!C)return[];break}}}if(g===p)if(C==null)l.error(g);else break;p=g}return t};l.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=l.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+\-]*)\))?/,
POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},relative:{"+":function(g,j){var o=typeof j==="string",m=o&&!/\W/.test(j);o=o&&!m;if(m)j=j.toLowerCase();m=0;for(var p=g.length,q;m<p;m++)if(q=g[m]){for(;(q=q.previousSibling)&&q.nodeType!==1;);g[m]=o||q&&q.nodeName.toLowerCase()===
j?q||false:q===j}o&&l.filter(j,g,true)},">":function(g,j){var o=typeof j==="string",m,p=0,q=g.length;if(o&&!/\W/.test(j))for(j=j.toLowerCase();p<q;p++){if(m=g[p]){o=m.parentNode;g[p]=o.nodeName.toLowerCase()===j?o:false}}else{for(;p<q;p++)if(m=g[p])g[p]=o?m.parentNode:m.parentNode===j;o&&l.filter(j,g,true)}},"":function(g,j,o){var m=e++,p=b,q;if(typeof j==="string"&&!/\W/.test(j)){q=j=j.toLowerCase();p=a}p("parentNode",j,m,g,q,o)},"~":function(g,j,o){var m=e++,p=b,q;if(typeof j==="string"&&!/\W/.test(j)){q=
j=j.toLowerCase();p=a}p("previousSibling",j,m,g,q,o)}},find:{ID:function(g,j,o){if(typeof j.getElementById!=="undefined"&&!o)return(g=j.getElementById(g[1]))&&g.parentNode?[g]:[]},NAME:function(g,j){if(typeof j.getElementsByName!=="undefined"){for(var o=[],m=j.getElementsByName(g[1]),p=0,q=m.length;p<q;p++)m[p].getAttribute("name")===g[1]&&o.push(m[p]);return o.length===0?null:o}},TAG:function(g,j){return j.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,j,o,m,p,q){g=" "+g[1].replace(/\\/g,
"")+" ";if(q)return g;q=0;for(var t;(t=j[q])!=null;q++)if(t)if(p^(t.className&&(" "+t.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))o||m.push(t);else if(o)j[q]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},CHILD:function(g){if(g[1]==="nth"){var j=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=j[1]+(j[2]||1)-0;g[3]=j[3]-0}g[0]=e++;return g},ATTR:function(g,j,o,
m,p,q){j=g[1].replace(/\\/g,"");if(!q&&n.attrMap[j])g[1]=n.attrMap[j];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,j,o,m,p){if(g[1]==="not")if((d.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=l(g[3],null,null,j);else{g=l.filter(g[3],j,o,true^p);o||m.push.apply(m,g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===
true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,j,o){return!!l(o[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===
g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},setFilters:{first:function(g,j){return j===0},last:function(g,j,o,m){return j===m.length-1},even:function(g,j){return j%2===0},odd:function(g,j){return j%2===1},lt:function(g,j,o){return j<o[3]-0},gt:function(g,j,o){return j>o[3]-0},nth:function(g,j,o){return o[3]-
0===j},eq:function(g,j,o){return o[3]-0===j}},filter:{PSEUDO:function(g,j,o,m){var p=j[1],q=n.filters[p];if(q)return q(g,o,j,m);else if(p==="contains")return(g.textContent||g.innerText||l.getText([g])||"").indexOf(j[3])>=0;else if(p==="not"){j=j[3];o=0;for(m=j.length;o<m;o++)if(j[o]===g)return false;return true}else l.error("Syntax error, unrecognized expression: "+p)},CHILD:function(g,j){var o=j[1],m=g;switch(o){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(o===
"first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":o=j[2];var p=j[3];if(o===1&&p===0)return true;var q=j[0],t=g.parentNode;if(t&&(t.sizcache!==q||!g.nodeIndex)){var x=0;for(m=t.firstChild;m;m=m.nextSibling)if(m.nodeType===1)m.nodeIndex=++x;t.sizcache=q}m=g.nodeIndex-p;return o===0?m===0:m%o===0&&m/o>=0}},ID:function(g,j){return g.nodeType===1&&g.getAttribute("id")===j},TAG:function(g,j){return j==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===
j},CLASS:function(g,j){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(j)>-1},ATTR:function(g,j){var o=j[1];o=n.attrHandle[o]?n.attrHandle[o](g):g[o]!=null?g[o]:g.getAttribute(o);var m=o+"",p=j[2],q=j[4];return o==null?p==="!=":p==="="?m===q:p==="*="?m.indexOf(q)>=0:p==="~="?(" "+m+" ").indexOf(q)>=0:!q?m&&o!==false:p==="!="?m!==q:p==="^="?m.indexOf(q)===0:p==="$="?m.substr(m.length-q.length)===q:p==="|="?m===q||m.substr(0,q.length+1)===q+"-":false},POS:function(g,j,o,m){var p=n.setFilters[j[2]];
if(p)return p(g,o,j,m)}}},s=n.match.POS,v=function(g,j){return"\\"+(j-0+1)},B;for(B in n.match){n.match[B]=RegExp(n.match[B].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[B]=RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[B].source.replace(/\\(\d+)/g,v))}var D=function(g,j){g=Array.prototype.slice.call(g,0);if(j){j.push.apply(j,g);return j}return g};try{Array.prototype.slice.call(u.documentElement.childNodes,0)}catch(H){D=function(g,j){var o=j||[],m=0;if(f.call(g)==="[object Array]")Array.prototype.push.apply(o,
g);else if(typeof g.length==="number")for(var p=g.length;m<p;m++)o.push(g[m]);else for(;g[m];m++)o.push(g[m]);return o}}var w,G;if(u.documentElement.compareDocumentPosition)w=function(g,j){if(g===j){h=true;return 0}if(!g.compareDocumentPosition||!j.compareDocumentPosition)return g.compareDocumentPosition?-1:1;return g.compareDocumentPosition(j)&4?-1:1};else{w=function(g,j){var o=[],m=[],p=g.parentNode,q=j.parentNode,t=p;if(g===j){h=true;return 0}else if(p===q)return G(g,j);else if(p){if(!q)return 1}else return-1;
for(;t;){o.unshift(t);t=t.parentNode}for(t=q;t;){m.unshift(t);t=t.parentNode}p=o.length;q=m.length;for(t=0;t<p&&t<q;t++)if(o[t]!==m[t])return G(o[t],m[t]);return t===p?G(g,m[t],-1):G(o[t],j,1)};G=function(g,j,o){if(g===j)return o;for(g=g.nextSibling;g;){if(g===j)return-1;g=g.nextSibling}return 1}}l.getText=function(g){for(var j="",o,m=0;g[m];m++){o=g[m];if(o.nodeType===3||o.nodeType===4)j+=o.nodeValue;else if(o.nodeType!==8)j+=l.getText(o.childNodes)}return j};(function(){var g=u.createElement("div"),
j="script"+(new Date).getTime();g.innerHTML="<a name='"+j+"'/>";var o=u.documentElement;o.insertBefore(g,o.firstChild);if(u.getElementById(j)){n.find.ID=function(m,p,q){if(typeof p.getElementById!=="undefined"&&!q)return(p=p.getElementById(m[1]))?p.id===m[1]||typeof p.getAttributeNode!=="undefined"&&p.getAttributeNode("id").nodeValue===m[1]?[p]:A:[]};n.filter.ID=function(m,p){var q=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&q&&q.nodeValue===p}}o.removeChild(g);
o=g=null})();(function(){var g=u.createElement("div");g.appendChild(u.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(j,o){var m=o.getElementsByTagName(j[1]);if(j[1]==="*"){for(var p=[],q=0;m[q];q++)m[q].nodeType===1&&p.push(m[q]);m=p}return m};g.innerHTML="<a href='#'></a>";if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(j){return j.getAttribute("href",2)};g=null})();u.querySelectorAll&&
function(){var g=l,j=u.createElement("div");j.innerHTML="<p class='TEST'></p>";if(!(j.querySelectorAll&&j.querySelectorAll(".TEST").length===0)){l=function(m,p,q,t){p=p||u;if(!t&&!l.isXML(p))if(p.nodeType===9)try{return D(p.querySelectorAll(m),q)}catch(x){}else if(p.nodeType===1&&p.nodeName.toLowerCase()!=="object"){var C=p.id,P=p.id="__sizzle__";try{return D(p.querySelectorAll("#"+P+" "+m),q)}catch(N){}finally{if(C)p.id=C;else p.removeAttribute("id")}}return g(m,p,q,t)};for(var o in g)l[o]=g[o];
j=null}}();(function(){var g=u.documentElement,j=g.matchesSelector||g.mozMatchesSelector||g.webkitMatchesSelector||g.msMatchesSelector,o=false;try{j.call(u.documentElement,":sizzle")}catch(m){o=true}if(j)l.matchesSelector=function(p,q){try{if(o||!n.match.PSEUDO.test(q))return j.call(p,q)}catch(t){}return l(q,null,null,[p]).length>0}})();(function(){var g=u.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===
0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(j,o,m){if(typeof o.getElementsByClassName!=="undefined"&&!m)return o.getElementsByClassName(j[1])};g=null}}})();l.contains=u.documentElement.contains?function(g,j){return g!==j&&(g.contains?g.contains(j):true)}:function(g,j){return!!(g.compareDocumentPosition(j)&16)};l.isXML=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false};var M=function(g,
j){for(var o=[],m="",p,q=j.nodeType?[j]:j;p=n.match.PSEUDO.exec(g);){m+=p[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;p=0;for(var t=q.length;p<t;p++)l(g,q[p],o);return l.filter(m,o)};c.find=l;c.expr=l.selectors;c.expr[":"]=c.expr.filters;c.unique=l.uniqueSort;c.text=l.getText;c.isXMLDoc=l.isXML;c.contains=l.contains})();var Wa=/Until$/,Xa=/^(?:parents|prevUntil|prevAll)/,Ya=/,/,Ja=/^.[^:#\[\.,]*$/,Za=Array.prototype.slice,$a=c.expr.match.POS;c.fn.extend({find:function(a){for(var b=this.pushStack("",
"find",a),d=0,e=0,f=this.length;e<f;e++){d=b.length;c.find(a,this[e],b);if(e>0)for(var h=d;h<b.length;h++)for(var k=0;k<d;k++)if(b[k]===b[h]){b.splice(h--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,e=b.length;d<e;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(ka(this,a,false),"not",a)},filter:function(a){return this.pushStack(ka(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,
b){var d=[],e,f,h=this[0];if(c.isArray(a)){var k={},l,n=1;if(h&&a.length){e=0;for(f=a.length;e<f;e++){l=a[e];k[l]||(k[l]=c.expr.match.POS.test(l)?c(l,b||this.context):l)}for(;h&&h.ownerDocument&&h!==b;){for(l in k){e=k[l];if(e.jquery?e.index(h)>-1:c(h).is(e))d.push({selector:l,elem:h,level:n})}h=h.parentNode;n++}}return d}k=$a.test(a)?c(a,b||this.context):null;e=0;for(f=this.length;e<f;e++)for(h=this[e];h;)if(k?k.index(h)>-1:c.find.matchesSelector(h,a)){d.push(h);break}else{h=h.parentNode;if(!h||
!h.ownerDocument||h===b)break}d=d.length>1?c.unique(d):d;return this.pushStack(d,"closest",a)},index:function(a){if(!a||typeof a==="string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var d=typeof a==="string"?c(a,b||this.context):c.makeArray(a),e=c.merge(this.get(),d);return this.pushStack(!d[0]||!d[0].parentNode||d[0].parentNode.nodeType===11||!e[0]||!e[0].parentNode||e[0].parentNode.nodeType===11?e:c.unique(e))},andSelf:function(){return this.add(this.prevObject)}});
c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",
d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,e){var f=c.map(this,b,d);Wa.test(a)||(e=d);if(e&&typeof e==="string")f=c.filter(e,f);f=this.length>1?c.unique(f):f;if((this.length>1||Ya.test(e))&&Xa.test(a))f=f.reverse();return this.pushStack(f,a,Za.call(arguments).join(","))}});
c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return b.length===1?c.find.matchesSelector(b[0],a)?[b[0]]:[]:c.find.matches(a,b)},dir:function(a,b,d){var e=[];for(a=a[b];a&&a.nodeType!==9&&(d===A||a.nodeType!==1||!c(a).is(d));){a.nodeType===1&&e.push(a);a=a[b]}return e},nth:function(a,b,d){b=b||1;for(var e=0;a;a=a[d])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var xa=/ jQuery\d+="(?:\d+|null)"/g,
$=/^\s+/,ya=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,za=/<([\w:]+)/,ab=/<tbody/i,bb=/<|&#?\w+;/,Aa=/<(?:script|object|embed|option|style)/i,Ba=/checked\s*(?:[^=]|=\s*.checked.)/i,cb=/\=([^="'>\s]+\/)>/g,O={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],
area:[1,"<map>","</map>"],_default:[0,"",""]};O.optgroup=O.option;O.tbody=O.tfoot=O.colgroup=O.caption=O.thead;O.th=O.td;if(!c.support.htmlSerialize)O._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==A)return this.empty().append((this[0]&&this[0].ownerDocument||u).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,
d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},
unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=
c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,e;(e=this[d])!=null;d++)if(!a||c.filter(a,[e]).length){if(!b&&e.nodeType===1){c.cleanData(e.getElementsByTagName("*"));
c.cleanData([e])}e.parentNode&&e.parentNode.removeChild(e)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,e=this.ownerDocument;if(!d){d=e.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(xa,"").replace(cb,'="$1">').replace($,
"")],e)[0]}else return this.cloneNode(true)});if(a===true){la(this,b);la(this.find("*"),b.find("*"))}return b},html:function(a){if(a===A)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(xa,""):null;else if(typeof a==="string"&&!Aa.test(a)&&(c.support.leadingWhitespace||!$.test(a))&&!O[(za.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(ya,"<$1></$2>");try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(e){this.empty().append(a)}}else c.isFunction(a)?
this.each(function(f){var h=c(this);h.html(a.call(this,f,h.html()))}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),e=d.html();d.replaceWith(a.call(this,b,e))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,
true)},domManip:function(a,b,d){var e,f,h=a[0],k=[],l;if(!c.support.checkClone&&arguments.length===3&&typeof h==="string"&&Ba.test(h))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(h))return this.each(function(s){var v=c(this);a[0]=h.call(this,s,b?v.html():A);v.domManip(a,b,d)});if(this[0]){e=h&&h.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:c.buildFragment(a,this,k);l=e.fragment;if(f=l.childNodes.length===1?l=l.firstChild:
l.firstChild){b=b&&c.nodeName(f,"tr");f=0;for(var n=this.length;f<n;f++)d.call(b?c.nodeName(this[f],"table")?this[f].getElementsByTagName("tbody")[0]||this[f].appendChild(this[f].ownerDocument.createElement("tbody")):this[f]:this[f],f>0||e.cacheable||this.length>1?l.cloneNode(true):l)}k.length&&c.each(k,Ka)}return this}});c.buildFragment=function(a,b,d){var e,f,h;b=b&&b[0]?b[0].ownerDocument||b[0]:u;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===u&&!Aa.test(a[0])&&(c.support.checkClone||
!Ba.test(a[0]))){f=true;if(h=c.fragments[a[0]])if(h!==1)e=h}if(!e){e=b.createDocumentFragment();c.clean(a,b,e,d)}if(f)c.fragments[a[0]]=h?e:1;return{fragment:e,cacheable:f}};c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var e=[];d=c(d);var f=this.length===1&&this[0].parentNode;if(f&&f.nodeType===11&&f.childNodes.length===1&&d.length===1){d[b](this[0]);return this}else{f=0;for(var h=
d.length;f<h;f++){var k=(f>0?this.clone(true):this).get();c(d[f])[b](k);e=e.concat(k)}return this.pushStack(e,a,d.selector)}}});c.extend({clean:function(a,b,d,e){b=b||u;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||u;for(var f=[],h=0,k;(k=a[h])!=null;h++){if(typeof k==="number")k+="";if(k){if(typeof k==="string"&&!bb.test(k))k=b.createTextNode(k);else if(typeof k==="string"){k=k.replace(ya,"<$1></$2>");var l=(za.exec(k)||["",""])[1].toLowerCase(),n=O[l]||O._default,
s=n[0],v=b.createElement("div");for(v.innerHTML=n[1]+k+n[2];s--;)v=v.lastChild;if(!c.support.tbody){s=ab.test(k);l=l==="table"&&!s?v.firstChild&&v.firstChild.childNodes:n[1]==="<table>"&&!s?v.childNodes:[];for(n=l.length-1;n>=0;--n)c.nodeName(l[n],"tbody")&&!l[n].childNodes.length&&l[n].parentNode.removeChild(l[n])}!c.support.leadingWhitespace&&$.test(k)&&v.insertBefore(b.createTextNode($.exec(k)[0]),v.firstChild);k=v.childNodes}if(k.nodeType)f.push(k);else f=c.merge(f,k)}}if(d)for(h=0;f[h];h++)if(e&&
c.nodeName(f[h],"script")&&(!f[h].type||f[h].type.toLowerCase()==="text/javascript"))e.push(f[h].parentNode?f[h].parentNode.removeChild(f[h]):f[h]);else{f[h].nodeType===1&&f.splice.apply(f,[h+1,0].concat(c.makeArray(f[h].getElementsByTagName("script"))));d.appendChild(f[h])}return f},cleanData:function(a){for(var b,d,e=c.cache,f=c.event.special,h=c.support.deleteExpando,k=0,l;(l=a[k])!=null;k++)if(!(l.nodeName&&c.noData[l.nodeName.toLowerCase()]))if(d=l[c.expando]){if((b=e[d])&&b.events)for(var n in b.events)f[n]?
c.event.remove(l,n):c.removeEvent(l,n,b.handle);if(h)delete l[c.expando];else l.removeAttribute&&l.removeAttribute(c.expando);delete e[d]}}});var Ca=/alpha\([^)]*\)/i,db=/opacity=([^)]*)/,eb=/-([a-z])/ig,fb=/([A-Z])/g,Da=/^-?\d+(?:px)?$/i,gb=/^-?\d/,hb={position:"absolute",visibility:"hidden",display:"block"},La=["Left","Right"],Ma=["Top","Bottom"],W,ib=u.defaultView&&u.defaultView.getComputedStyle,jb=function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){if(arguments.length===2&&b===A)return this;
return c.access(this,a,b,true,function(d,e,f){return f!==A?c.style(d,e,f):c.css(d,e)})};c.extend({cssHooks:{opacity:{get:function(a,b){if(b){var d=W(a,"opacity","opacity");return d===""?"1":d}else return a.style.opacity}}},cssNumber:{zIndex:true,fontWeight:true,opacity:true,zoom:true,lineHeight:true},cssProps:{"float":c.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,d,e){if(!(!a||a.nodeType===3||a.nodeType===8||!a.style)){var f,h=c.camelCase(b),k=a.style,l=c.cssHooks[h];b=c.cssProps[h]||
h;if(d!==A){if(!(typeof d==="number"&&isNaN(d)||d==null)){if(typeof d==="number"&&!c.cssNumber[h])d+="px";if(!l||!("set"in l)||(d=l.set(a,d))!==A)try{k[b]=d}catch(n){}}}else{if(l&&"get"in l&&(f=l.get(a,false,e))!==A)return f;return k[b]}}},css:function(a,b,d){var e,f=c.camelCase(b),h=c.cssHooks[f];b=c.cssProps[f]||f;if(h&&"get"in h&&(e=h.get(a,true,d))!==A)return e;else if(W)return W(a,b,f)},swap:function(a,b,d){var e={},f;for(f in b){e[f]=a.style[f];a.style[f]=b[f]}d.call(a);for(f in b)a.style[f]=
e[f]},camelCase:function(a){return a.replace(eb,jb)}});c.curCSS=c.css;c.each(["height","width"],function(a,b){c.cssHooks[b]={get:function(d,e,f){var h;if(e){if(d.offsetWidth!==0)h=ma(d,b,f);else c.swap(d,hb,function(){h=ma(d,b,f)});return h+"px"}},set:function(d,e){if(Da.test(e)){e=parseFloat(e);if(e>=0)return e+"px"}else return e}}});if(!c.support.opacity)c.cssHooks.opacity={get:function(a,b){return db.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":
b?"1":""},set:function(a,b){var d=a.style;d.zoom=1;var e=c.isNaN(b)?"":"alpha(opacity="+b*100+")",f=d.filter||"";d.filter=Ca.test(f)?f.replace(Ca,e):d.filter+" "+e}};if(ib)W=function(a,b,d){var e;d=d.replace(fb,"-$1").toLowerCase();if(!(b=a.ownerDocument.defaultView))return A;if(b=b.getComputedStyle(a,null)){e=b.getPropertyValue(d);if(e===""&&!c.contains(a.ownerDocument.documentElement,a))e=c.style(a,d)}return e};else if(u.documentElement.currentStyle)W=function(a,b){var d,e,f=a.currentStyle&&a.currentStyle[b],
h=a.style;if(!Da.test(f)&&gb.test(f)){d=h.left;e=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;h.left=b==="fontSize"?"1em":f||0;f=h.pixelLeft+"px";h.left=d;a.runtimeStyle.left=e}return f};if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=a.offsetHeight;return a.offsetWidth===0&&b===0||!c.support.reliableHiddenOffsets&&(a.style.display||c.css(a,"display"))==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var kb=c.now(),lb=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
mb=/^(?:select|textarea)/i,nb=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,ob=/^(?:GET|HEAD|DELETE)$/,Na=/\[\]$/,T=/\=\?(&|$)/,ia=/\?/,pb=/([?&])_=[^&]*/,qb=/^(\w+:)?\/\/([^\/?#]+)/,rb=/%20/g,sb=/#.*$/,Ea=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!=="string"&&Ea)return Ea.apply(this,arguments);else if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var f=a.slice(e,a.length);a=a.slice(0,e)}e="GET";if(b)if(c.isFunction(b)){d=
b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);e="POST"}var h=this;c.ajax({url:a,type:e,dataType:"html",data:b,complete:function(k,l){if(l==="success"||l==="notmodified")h.html(f?c("<div>").append(k.responseText.replace(lb,"")).find(f):k.responseText);d&&h.each(d,[k.responseText,l,k])}});return this},serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&
!this.disabled&&(this.checked||mb.test(this.nodeName)||nb.test(this.type))}).map(function(a,b){var d=c(this).val();return d==null?null:c.isArray(d)?c.map(d,function(e){return{name:b.name,value:e}}):{name:b.name,value:d}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:e})},
getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:e})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return new E.XMLHttpRequest},accepts:{xml:"application/xml, text/xml",html:"text/html",
script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},ajax:function(a){var b=c.extend(true,{},c.ajaxSettings,a),d,e,f,h=b.type.toUpperCase(),k=ob.test(h);b.url=b.url.replace(sb,"");b.context=a&&a.context!=null?a.context:b;if(b.data&&b.processData&&typeof b.data!=="string")b.data=c.param(b.data,b.traditional);if(b.dataType==="jsonp"){if(h==="GET")T.test(b.url)||(b.url+=(ia.test(b.url)?"&":"?")+(b.jsonp||"callback")+"=?");else if(!b.data||
!T.test(b.data))b.data=(b.data?b.data+"&":"")+(b.jsonp||"callback")+"=?";b.dataType="json"}if(b.dataType==="json"&&(b.data&&T.test(b.data)||T.test(b.url))){d=b.jsonpCallback||"jsonp"+kb++;if(b.data)b.data=(b.data+"").replace(T,"="+d+"$1");b.url=b.url.replace(T,"="+d+"$1");b.dataType="script";var l=E[d];E[d]=function(m){f=m;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);if(c.isFunction(l))l(m);else{E[d]=A;try{delete E[d]}catch(p){}}v&&v.removeChild(B)}}if(b.dataType==="script"&&b.cache===null)b.cache=
false;if(b.cache===false&&h==="GET"){var n=c.now(),s=b.url.replace(pb,"$1_="+n);b.url=s+(s===b.url?(ia.test(b.url)?"&":"?")+"_="+n:"")}if(b.data&&h==="GET")b.url+=(ia.test(b.url)?"&":"?")+b.data;b.global&&c.active++===0&&c.event.trigger("ajaxStart");n=(n=qb.exec(b.url))&&(n[1]&&n[1]!==location.protocol||n[2]!==location.host);if(b.dataType==="script"&&h==="GET"&&n){var v=u.getElementsByTagName("head")[0]||u.documentElement,B=u.createElement("script");if(b.scriptCharset)B.charset=b.scriptCharset;B.src=
b.url;if(!d){var D=false;B.onload=B.onreadystatechange=function(){if(!D&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){D=true;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);B.onload=B.onreadystatechange=null;v&&B.parentNode&&v.removeChild(B)}}}v.insertBefore(B,v.firstChild);return A}var H=false,w=b.xhr();if(w){b.username?w.open(h,b.url,b.async,b.username,b.password):w.open(h,b.url,b.async);try{if(b.data!=null&&!k||a&&a.contentType)w.setRequestHeader("Content-Type",
b.contentType);if(b.ifModified){c.lastModified[b.url]&&w.setRequestHeader("If-Modified-Since",c.lastModified[b.url]);c.etag[b.url]&&w.setRequestHeader("If-None-Match",c.etag[b.url])}n||w.setRequestHeader("X-Requested-With","XMLHttpRequest");w.setRequestHeader("Accept",b.dataType&&b.accepts[b.dataType]?b.accepts[b.dataType]+", */*; q=0.01":b.accepts._default)}catch(G){}if(b.beforeSend&&b.beforeSend.call(b.context,w,b)===false){b.global&&c.active--===1&&c.event.trigger("ajaxStop");w.abort();return false}b.global&&
c.triggerGlobal(b,"ajaxSend",[w,b]);var M=w.onreadystatechange=function(m){if(!w||w.readyState===0||m==="abort"){H||c.handleComplete(b,w,e,f);H=true;if(w)w.onreadystatechange=c.noop}else if(!H&&w&&(w.readyState===4||m==="timeout")){H=true;w.onreadystatechange=c.noop;e=m==="timeout"?"timeout":!c.httpSuccess(w)?"error":b.ifModified&&c.httpNotModified(w,b.url)?"notmodified":"success";var p;if(e==="success")try{f=c.httpData(w,b.dataType,b)}catch(q){e="parsererror";p=q}if(e==="success"||e==="notmodified")d||
c.handleSuccess(b,w,e,f);else c.handleError(b,w,e,p);d||c.handleComplete(b,w,e,f);m==="timeout"&&w.abort();if(b.async)w=null}};try{var g=w.abort;w.abort=function(){w&&g.call&&g.call(w);M("abort")}}catch(j){}b.async&&b.timeout>0&&setTimeout(function(){w&&!H&&M("timeout")},b.timeout);try{w.send(k||b.data==null?null:b.data)}catch(o){c.handleError(b,w,null,o);c.handleComplete(b,w,e,f)}b.async||M();return w}},param:function(a,b){var d=[],e=function(h,k){k=c.isFunction(k)?k():k;d[d.length]=encodeURIComponent(h)+
"="+encodeURIComponent(k)};if(b===A)b=c.ajaxSettings.traditional;if(c.isArray(a)||a.jquery)c.each(a,function(){e(this.name,this.value)});else for(var f in a)ca(f,a[f],b,e);return d.join("&").replace(rb,"+")}});c.extend({active:0,lastModified:{},etag:{},handleError:function(a,b,d,e){a.error&&a.error.call(a.context,b,d,e);a.global&&c.triggerGlobal(a,"ajaxError",[b,a,e])},handleSuccess:function(a,b,d,e){a.success&&a.success.call(a.context,e,d,b);a.global&&c.triggerGlobal(a,"ajaxSuccess",[b,a])},handleComplete:function(a,
b,d){a.complete&&a.complete.call(a.context,b,d);a.global&&c.triggerGlobal(a,"ajaxComplete",[b,a]);a.global&&c.active--===1&&c.event.trigger("ajaxStop")},triggerGlobal:function(a,b,d){(a.context&&a.context.url==null?c(a.context):c.event).trigger(b,d)},httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===1223}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),e=a.getResponseHeader("Etag");
if(d)c.lastModified[b]=d;if(e)c.etag[b]=e;return a.status===304},httpData:function(a,b,d){var e=a.getResponseHeader("content-type")||"",f=b==="xml"||!b&&e.indexOf("xml")>=0;a=f?a.responseXML:a.responseText;f&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b==="json"||!b&&e.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&e.indexOf("javascript")>=0)c.globalEval(a);return a}});if(E.ActiveXObject)c.ajaxSettings.xhr=
function(){if(E.location.protocol!=="file:")try{return new E.XMLHttpRequest}catch(a){}try{return new E.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}};c.support.ajax=!!c.ajaxSettings.xhr();var da={},tb=/^(?:toggle|show|hide)$/,ub=/^([+\-]=)?([\d+.\-]+)(.*)$/,aa,na=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b,d){if(a||a===0)return this.animate(S("show",3),a,b,d);else{a=
0;for(b=this.length;a<b;a++){if(!c.data(this[a],"olddisplay")&&this[a].style.display==="none")this[a].style.display="";this[a].style.display===""&&c.css(this[a],"display")==="none"&&c.data(this[a],"olddisplay",oa(this[a].nodeName))}for(a=0;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b,d){if(a||a===0)return this.animate(S("hide",3),a,b,d);else{a=0;for(b=this.length;a<b;a++){d=c.css(this[a],"display");d!=="none"&&c.data(this[a],"olddisplay",d)}for(a=
0;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b,d){var e=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||e?this.each(function(){var f=e?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(S("toggle",3),a,b,d);return this},fadeTo:function(a,b,d,e){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d,e)},animate:function(a,b,d,e){var f=c.speed(b,d,e);if(c.isEmptyObject(a))return this.each(f.complete);
return this[f.queue===false?"each":"queue"](function(){var h=c.extend({},f),k,l=this.nodeType===1,n=l&&c(this).is(":hidden"),s=this;for(k in a){var v=c.camelCase(k);if(k!==v){a[v]=a[k];delete a[k];k=v}if(a[k]==="hide"&&n||a[k]==="show"&&!n)return h.complete.call(this);if(l&&(k==="height"||k==="width")){h.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY];if(c.css(this,"display")==="inline"&&c.css(this,"float")==="none")if(c.support.inlineBlockNeedsLayout)if(oa(this.nodeName)===
"inline")this.style.display="inline-block";else{this.style.display="inline";this.style.zoom=1}else this.style.display="inline-block"}if(c.isArray(a[k])){(h.specialEasing=h.specialEasing||{})[k]=a[k][1];a[k]=a[k][0]}}if(h.overflow!=null)this.style.overflow="hidden";h.curAnim=c.extend({},a);c.each(a,function(B,D){var H=new c.fx(s,h,B);if(tb.test(D))H[D==="toggle"?n?"show":"hide":D](a);else{var w=ub.exec(D),G=H.cur(true)||0;if(w){var M=parseFloat(w[2]),g=w[3]||"px";if(g!=="px"){c.style(s,B,(M||1)+g);
G=(M||1)/H.cur(true)*G;c.style(s,B,G+g)}if(w[1])M=(w[1]==="-="?-1:1)*M+G;H.custom(G,M,g)}else H.custom(G,D,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);this.each(function(){for(var e=d.length-1;e>=0;e--)if(d[e].elem===this){b&&d[e](true);d.splice(e,1)}});b||this.dequeue();return this}});c.each({slideDown:S("show",1),slideUp:S("hide",1),slideToggle:S("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,e,f){return this.animate(b,
d,e,f)}});c.extend({speed:function(a,b,d){var e=a&&typeof a==="object"?c.extend({},a):{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};e.duration=c.fx.off?0:typeof e.duration==="number"?e.duration:e.duration in c.fx.speeds?c.fx.speeds[e.duration]:c.fx.speeds._default;e.old=e.complete;e.complete=function(){e.queue!==false&&c(this).dequeue();c.isFunction(e.old)&&e.old.call(this)};return e},easing:{linear:function(a,b,d,e){return d+e*a},swing:function(a,b,d,e){return(-Math.cos(a*
Math.PI)/2+0.5)*e+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||c.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a=parseFloat(c.css(this.elem,this.prop));return a&&a>-1E4?a:0},custom:function(a,b,d){function e(h){return f.step(h)}
this.startTime=c.now();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;this.pos=this.state=0;var f=this;a=c.fx;e.elem=this.elem;if(e()&&c.timers.push(e)&&!aa)aa=setInterval(a.tick,a.interval)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;
this.custom(this.cur(),0)},step:function(a){var b=c.now(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var e in this.options.curAnim)if(this.options.curAnim[e]!==true)d=false;if(d){if(this.options.overflow!=null&&!c.support.shrinkWrapBlocks){var f=this.elem,h=this.options;c.each(["","X","Y"],function(l,n){f.style["overflow"+n]=h.overflow[l]})}this.options.hide&&c(this.elem).hide();if(this.options.hide||
this.options.show)for(var k in this.options.curAnim)c.style(this.elem,k,this.options.orig[k]);this.options.complete.call(this.elem)}return false}else{a=b-this.startTime;this.state=a/this.options.duration;b=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||b](this.state,a,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=
c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||c.fx.stop()},interval:13,stop:function(){clearInterval(aa);aa=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===
b.elem}).length};var vb=/^t(?:able|d|h)$/i,Fa=/^(?:body|html)$/i;c.fn.offset="getBoundingClientRect"in u.documentElement?function(a){var b=this[0],d;if(a)return this.each(function(k){c.offset.setOffset(this,a,k)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);try{d=b.getBoundingClientRect()}catch(e){}var f=b.ownerDocument,h=f.documentElement;if(!d||!c.contains(h,b))return d||{top:0,left:0};b=f.body;f=ea(f);return{top:d.top+(f.pageYOffset||c.support.boxModel&&
h.scrollTop||b.scrollTop)-(h.clientTop||b.clientTop||0),left:d.left+(f.pageXOffset||c.support.boxModel&&h.scrollLeft||b.scrollLeft)-(h.clientLeft||b.clientLeft||0)}}:function(a){var b=this[0];if(a)return this.each(function(s){c.offset.setOffset(this,a,s)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,e=b.ownerDocument,f,h=e.documentElement,k=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;
for(var l=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==k&&b!==h;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;f=e?e.getComputedStyle(b,null):b.currentStyle;l-=b.scrollTop;n-=b.scrollLeft;if(b===d){l+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&vb.test(b.nodeName))){l+=parseFloat(f.borderTopWidth)||0;n+=parseFloat(f.borderLeftWidth)||0}d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&f.overflow!=="visible"){l+=
parseFloat(f.borderTopWidth)||0;n+=parseFloat(f.borderLeftWidth)||0}f=f}if(f.position==="relative"||f.position==="static"){l+=k.offsetTop;n+=k.offsetLeft}if(c.offset.supportsFixedPosition&&f.position==="fixed"){l+=Math.max(h.scrollTop,k.scrollTop);n+=Math.max(h.scrollLeft,k.scrollLeft)}return{top:l,left:n}};c.offset={initialize:function(){var a=u.body,b=u.createElement("div"),d,e,f,h=parseFloat(c.css(a,"marginTop"))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",
height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";a.insertBefore(b,a.firstChild);d=b.firstChild;e=d.firstChild;f=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=e.offsetTop!==5;this.doesAddBorderForTableAndCells=
f.offsetTop===5;e.style.position="fixed";e.style.top="20px";this.supportsFixedPosition=e.offsetTop===20||e.offsetTop===15;e.style.position=e.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=e.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==h;a.removeChild(b);c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.css(a,
"marginTop"))||0;d+=parseFloat(c.css(a,"marginLeft"))||0}return{top:b,left:d}},setOffset:function(a,b,d){var e=c.css(a,"position");if(e==="static")a.style.position="relative";var f=c(a),h=f.offset(),k=c.css(a,"top"),l=c.css(a,"left"),n=e==="absolute"&&c.inArray("auto",[k,l])>-1;e={};var s={};if(n)s=f.position();k=n?s.top:parseInt(k,10)||0;l=n?s.left:parseInt(l,10)||0;if(c.isFunction(b))b=b.call(a,d,h);if(b.top!=null)e.top=b.top-h.top+k;if(b.left!=null)e.left=b.left-h.left+l;"using"in b?b.using.call(a,
e):f.css(e)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),e=Fa.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.css(a,"marginTop"))||0;d.left-=parseFloat(c.css(a,"marginLeft"))||0;e.top+=parseFloat(c.css(b[0],"borderTopWidth"))||0;e.left+=parseFloat(c.css(b[0],"borderLeftWidth"))||0;return{top:d.top-e.top,left:d.left-e.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||u.body;a&&!Fa.test(a.nodeName)&&
c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(e){var f=this[0],h;if(!f)return null;if(e!==A)return this.each(function(){if(h=ea(this))h.scrollTo(!a?e:c(h).scrollLeft(),a?e:c(h).scrollTop());else this[d]=e});else return(h=ea(f))?"pageXOffset"in h?h[a?"pageYOffset":"pageXOffset"]:c.support.boxModel&&h.document.documentElement[d]||h.document.body[d]:f[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();
c.fn["inner"+b]=function(){return this[0]?parseFloat(c.css(this[0],d,"padding")):null};c.fn["outer"+b]=function(e){return this[0]?parseFloat(c.css(this[0],d,e?"margin":"border")):null};c.fn[d]=function(e){var f=this[0];if(!f)return e==null?null:this;if(c.isFunction(e))return this.each(function(h){var k=c(this);k[d](e.call(this,h,k[d]()))});return c.isWindow(f)?f.document.compatMode==="CSS1Compat"&&f.document.documentElement["client"+b]||f.document.body["client"+b]:f.nodeType===9?Math.max(f.documentElement["client"+
b],f.body["scroll"+b],f.documentElement["scroll"+b],f.body["offset"+b],f.documentElement["offset"+b]):e===A?parseFloat(c.css(f,d)):this.css(d,typeof e==="string"?e:e+"px")}})})(window);

//]]>
</script>
<script id="jqueryArea" type="text/javascript">
//<![CDATA[
/*
jQuery.encoding.digests.sha1.js

SHA-1 digest and associated utility functions

Copyright (c) UnaMesa Association 2009

Dual licensed under the MIT and GPL licenses:
  http://www.opensource.org/licenses/mit-license.php
  http://www.gnu.org/licenses/gpl.html
*/

(function($) {

if(!$.encoding)
	$.encoding = {};
	$.extend($.encoding,{
		strToBe32s: function(str) {
			// Convert a string to an array of big-endian 32-bit words
			var be=[];
			var len=Math.floor(str.length/4);
			var i, j;
			for(i=0, j=0; i<len; i++, j+=4) {
				be[i]=((str.charCodeAt(j)&0xff) << 24)|((str.charCodeAt(j+1)&0xff) << 16)|((str.charCodeAt(j+2)&0xff) << 8)|(str.charCodeAt(j+3)&0xff);
			}
			while(j<str.length) {
				be[j>>2] |= (str.charCodeAt(j)&0xff)<<(24-(j*8)%32);
				j++;
			}
			return be;
		},
		be32sToStr: function(be) {
			// Convert an array of big-endian 32-bit words to a string
			var str='';
			for(var i=0;i<be.length*32;i+=8) {
				str += String.fromCharCode((be[i>>5]>>>(24-i%32)) & 0xff);
			}
			return str;
		},
		be32sToHex: function(be) {
			// Convert an array of big-endian 32-bit words to a hex string
			var hex='0123456789ABCDEF';
			var str='';
			for(var i=0;i<be.length*4;i++) {
				str += hex.charAt((be[i>>2]>>((3-i%4)*8+4))&0xF) + hex.charAt((be[i>>2]>>((3-i%4)*8))&0xF);
			}
			return str;
		}
	});
})(jQuery);


(function($) {

if(!$.encoding.digests)
	$.encoding.digests = {};
	$.extend($.encoding.digests,{
		hexSha1Str: function(str) {
			// Return, in hex, the SHA-1 hash of a string
			return $.encoding.be32sToHex($.encoding.digests.sha1Str(str));
		},
		sha1Str: function(str) {
			// Return the SHA-1 hash of a string
			return sha1($.encoding.strToBe32s(str),str.length);
		},
		sha1: function(x,blen) {
			// Calculate the SHA-1 hash of an array of blen bytes of big-endian 32-bit words
			return sha1($.encoding.strToBe32s(str),str.length);
		}
	});

	// Private functions.
	function sha1(x,blen) {
		// Calculate the SHA-1 hash of an array of blen bytes of big-endian 32-bit words
		function add32(a,b) {
			// Add 32-bit integers, wrapping at 32 bits
			// Uses 16-bit operations internally to work around bugs in some JavaScript interpreters.
			var lsw=(a&0xFFFF)+(b&0xFFFF);
			var msw=(a>>16)+(b>>16)+(lsw>>16);
			return (msw<<16)|(lsw&0xFFFF);
		}
		function AA(a,b,c,d,e) {
			// Cryptographic round helper function. Add five 32-bit integers, wrapping at 32 bits, second parameter is rotated left 5 bits before the addition
			// Uses 16-bit operations internally to work around bugs in some JavaScript interpreters.
			b=(b>>>27)|(b<<5);
			var lsw=(a&0xFFFF)+(b&0xFFFF)+(c&0xFFFF)+(d&0xFFFF)+(e&0xFFFF);
			var msw=(a>>16)+(b>>16)+(c>>16)+(d>>16)+(e>>16)+(lsw>>16);
			return (msw<<16)|(lsw&0xFFFF);
		}
		function RR(w,j) {
			// Cryptographic round helper function.
			var n=w[j-3]^w[j-8]^w[j-14]^w[j-16];
			return (n>>>31)|(n<<1);
		}

		var len=blen*8;
		x[len>>5] |= 0x80 << (24-len%32);
		x[((len+64>>9)<<4)+15]=len;
		var w=new Array(80);

		var k1=0x5A827999;
		var k2=0x6ED9EBA1;
		var k3=0x8F1BBCDC;
		var k4=0xCA62C1D6;

		var h0=0x67452301;
		var h1=0xEFCDAB89;
		var h2=0x98BADCFE;
		var h3=0x10325476;
		var h4=0xC3D2E1F0;

		for(var i=0;i<x.length;i+=16) {
			var j=0;
			var t;
			var a=h0;
			var b=h1;
			var c=h2;
			var d=h3;
			var e=h4;
			while(j<16) {
				w[j]=x[i+j];
				t=AA(e,a,d^(b&(c^d)),w[j],k1);
				e=d; d=c; c=(b>>>2)|(b<<30); b=a; a=t; j++;
			}
			while(j<20) {
				w[j]=RR(w,j);
				t=AA(e,a,d^(b&(c^d)),w[j],k1);
				e=d; d=c; c=(b>>>2)|(b<<30); b=a; a=t; j++;
			}
			while(j<40) {
				w[j]=RR(w,j);
				t=AA(e,a,b^c^d,w[j],k2);
				e=d; d=c; c=(b>>>2)|(b<<30); b=a; a=t; j++;
			}
			while(j<60) {
				w[j]=RR(w,j);
				t=AA(e,a,(b&c)|(d&(b|c)),w[j],k3);
				e=d; d=c; c=(b>>>2)|(b<<30); b=a; a=t; j++;
			}
			while(j<80) {
				w[j]=RR(w,j);
				t=AA(e,a,b^c^d,w[j],k4);
				e=d; d=c; c=(b>>>2)|(b<<30); b=a; a=t; j++;
			}
			h0=add32(h0,a);
			h1=add32(h1,b);
			h2=add32(h2,c);
			h3=add32(h3,d);
			h4=add32(h4,e);
		}
		return [h0,h1,h2,h3,h4];
	}
})(jQuery);
/*
jQuery.twStylesheet.js

jQuery plugin to dynamically insert CSS rules into a document

Usage:
  jQuery.twStylesheet applies style definitions
  jQuery.twStylesheet.remove neutralizes style definitions

Copyright (c) UnaMesa Association 2009

Triple licensed under the BSD, MIT and GPL licenses:
  http://www.opensource.org/licenses/bsd-license.php
  http://www.opensource.org/licenses/mit-license.php
  http://www.gnu.org/licenses/gpl.html
*/

(function($) {

var defaultId = "customStyleSheet"; // XXX: rename to dynamicStyleSheet?

// Add or replace a style sheet
// css argument is a string of CSS rule sets
// options.id is an optional name identifying the style sheet
// options.doc is an optional document reference
// N.B.: Uses DOM methods instead of jQuery to ensure cross-browser comaptibility.
$.twStylesheet = function(css, options) {
	options = options || {};
	var id = options.id || defaultId;
	var doc = options.doc || document;
	var el = doc.getElementById(id);
	if(doc.createStyleSheet) { // IE-specific handling
		if(el) {
			el.cssText = css;
		} else {
			doc.getElementsByTagName("head")[0].insertAdjacentHTML("beforeEnd",
				'&nbsp;<style id="' + id + '" type="text/css">' + css + '</style>'); // fails without &nbsp;
		}
	} else { // modern browsers
		if(el) {
			el.replaceChild(doc.createTextNode(css), el.firstChild);
		} else {
			el = doc.createElement("style");
			el.type = "text/css";
			el.id = id;
			el.appendChild(doc.createTextNode(css));
			doc.getElementsByTagName("head")[0].appendChild(el);
		}
	}
};

// Remove existing style sheet
// options.id is an optional name identifying the style sheet
// options.doc is an optional document reference
$.twStylesheet.remove = function(options) {
	options = options || {};
	var id = options.id || defaultId;
	var doc = options.doc || document;
	var el = doc.getElementById(id);
	if(el) {
		el.parentNode.removeChild(el);
	}
};

})(jQuery);
//]]>
</script>
<script type="text/javascript">
//<![CDATA[
if(useJavaSaver)
	document.write("<applet style='position:absolute;left:-1px' name='TiddlySaver' code='TiddlySaver.class' archive='TiddlySaver.jar' width='1' height='1'></applet>");
//]]>
</script>
<!--POST-SCRIPT-START-->
<p/><p/>
<script type="text/javascript">

  var _gaq = _gaq || [];
  _gaq.push(['_setAccount', 'UA-24724965-1']);
  _gaq.push(['_setDomainName', 'openresty.org']);
  _gaq.push(['_trackPageview']);

  var ga_f = function() {
    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
  };
  setTimeout(ga_f, 200);

</script>
<!--POST-SCRIPT-END-->
</body>
</html>
